从Java/JPA调用存储过程

use*_*514 91 java sql jboss stored-procedures jpa

我正在编写一个简单的Web应用程序来调用存储过程并检索一些数据.它是一个非常简单的应用程序,它与客户端的数据库进行交互.我们传递员工ID和公司ID,存储过程将返回员工详细信息.

Web应用程序无法更新/删除数据并且正在使用SQL Server.

我正在Jboss AS中部署我的Web应用程序.我应该使用JPA来访问存储过程或CallableStatement.在这种情况下使用JPA的任何优点.

另外,调用此存储过程的sql语句是什么.我之前从未使用过存储过程,而且我正在努力解决这个问题.谷歌没有多大帮助.

这是存储过程:

CREATE procedure getEmployeeDetails (@employeeId int, @companyId int)
as
begin
    select firstName, 
           lastName, 
           gender, 
           address
      from employee et
     where et.employeeId = @employeeId
       and et.companyId = @companyId
end
Run Code Online (Sandbox Code Playgroud)

更新:

对于其他任何使用JPA调用存储过程的问题.

Query query = em.createNativeQuery("{call getEmployeeDetails(?,?)}",
                                   EmployeeDetails.class)           
                                   .setParameter(1, employeeId)
                                   .setParameter(2, companyId);

List<EmployeeDetails> result = query.getResultList();
Run Code Online (Sandbox Code Playgroud)

我注意到的事情:

  1. 参数名称对我不起作用,因此请尝试使用参数索引.
  2. 更正sql语句{call sp_name(?,?)}而不是 call sp_name(?,?)
  3. 如果存储过程返回结果集,即使您只知道一行,也getSingleResult不会工作
  4. 传递resultSetMapping名称或结果类详细信息

Pau*_*Wee 55

JPA 2.1现在支持存储过程,请在此处阅读Java文档.

例:

StoredProcedureQuery storedProcedure = em.createStoredProcedureQuery("sales_tax");
// set parameters
storedProcedure.registerStoredProcedureParameter("subtotal", Double.class, ParameterMode.IN);
storedProcedure.registerStoredProcedureParameter("tax", Double.class, ParameterMode.OUT);
storedProcedure.setParameter("subtotal", 1f);
// execute SP
storedProcedure.execute();
// get result
Double tax = (Double)storedProcedure.getOutputParameterValue("tax");
Run Code Online (Sandbox Code Playgroud)

见详细的例子在这里.


Pas*_*ent 22

我正在Jboss AS中部署我的Web应用程序.我应该使用JPA来访问存储过程或CallableStatement.在这种情况下使用JPA的任何优点.

JPA并不真正支持它,但它是可行的.我仍然不会这样:

  • 使用JPA只是为了在一些bean中映射存储过程调用的结果实在是太过分了,
  • 特别是考虑到JPA不适合调用存储过程(语法非常冗长).

因此,我宁愿考虑使用Spring支持JDBC数据访问,也可以考虑使用像MyBatis这样的数据映射器,或者考虑到应用程序的简单性,使用原始JDBC和CallableStatement.实际上,JDBC可能是我的选择.这是一个基本的启动示例:

CallableStatement cstmt = con.prepareCall("{call getEmployeeDetails(?, ?)}");
cstmt.setInt("employeeId", 123);
cstmt.setInt("companyId", 456);
ResultSet rs = cstmt.executeQuery();
Run Code Online (Sandbox Code Playgroud)

参考


Sea*_*oyd 9

您需要将参数传递给存储过程.

它应该像这样工作:

    List result = em
      .createNativeQuery("call getEmployeeDetails(:employeeId,:companyId)")
      .setParameter("emplyoyeeId", 123L)
      .setParameter("companyId", 456L)
      .getResultList();
Run Code Online (Sandbox Code Playgroud)

更新:

或许它不应该.

在Book EJB3 in Action中,它在第383页上说,JPA不支持存储过程(页面只是一个预览,你没有得到全文,整本书可以在几个地方下载,包括这个,我不知道这是否合法).

无论如何,文本是这样的:

JPA和数据库存储过程

如果您是SQL的忠实粉丝,您可能愿意利用数据库存储过程的强大功能.遗憾的是,JPA不支持存储过程,您必须依赖持久性提供程序的专有功能.但是,您可以使用简单的存储函数(不带out参数)和本机SQL查询.

  • 应该是"{call getEmployeeDetails(:employeeId,:companyId)}",对于SQL服务器,它必须有花括号. (2认同)

小智 9

如何使用JPA检索存储过程输出参数(2.0需要EclipseLink导入,2.1不需要)

虽然这个答案确实详细说明了从存储过程中返回一个记录集,但我在这里发帖,因为我花了很多时间才弄清楚这个线程对我有帮助.

我的应用程序使用Eclipselink-2.3.1,但我将强制升级到Eclipselink-2.5.0,因为JPA 2.1对存储过程有更好的支持.

使用EclipseLink-2.3.1/JPA-2.0:依赖于实现

此方法需要从"org.eclipse.persistence"导入EclipseLink类,因此它特定于Eclipselink实现.

我在" http://www.yenlo.nl/en/calling-oracle-stored-procedures-from-eclipselink-with-multiple-out-parameters " 找到了它.

StoredProcedureCall storedProcedureCall = new StoredProcedureCall();
storedProcedureCall.setProcedureName("mypackage.myprocedure");
storedProcedureCall.addNamedArgument("i_input_1"); // Add input argument name.
storedProcedureCall.addNamedOutputArgument("o_output_1"); // Add output parameter name.
DataReadQuery query = new DataReadQuery();
query.setCall(storedProcedureCall);
query.addArgument("i_input_1"); // Add input argument names (again);
List<Object> argumentValues = new ArrayList<Object>();
argumentValues.add("valueOf_i_input_1"); // Add input argument values.
JpaEntityManager jpaEntityManager = (JpaEntityManager) getEntityManager();
Session session = jpaEntityManager.getActiveSession();
List<?> results = (List<?>) session.executeQuery(query, argumentValues);
DatabaseRecord record = (DatabaseRecord) results.get(0);
String result = String.valueOf(record.get("o_output_1")); // Get output parameter
Run Code Online (Sandbox Code Playgroud)

使用EclipseLink-2.5.0/JPA-2.1:独立于实现(已在此线程中记录)

此方法独立于实现(不需要Eclipslink导入).

StoredProcedureQuery query = getEntityManager().createStoredProcedureQuery("mypackage.myprocedure");
query.registerStoredProcedureParameter("i_input_1", String.class, ParameterMode.IN);
query.registerStoredProcedureParameter("o_output_1", String.class, ParameterMode.OUT);
query.setParameter("i_input_1", "valueOf_i_input_1");
boolean queryResult = query.execute();
String result = String.valueOf(query.getOutputParameterValue("o_output_1"));
Run Code Online (Sandbox Code Playgroud)

  • 啊啊,我的眼睛疼了.这并不比JDBC好多少,是吗? (7认同)

Dmi*_*nyi 6

对我来说,只有以下内容适用于Oracle 11g和Glassfish 2.1(Toplink):

Query query = entityManager.createNativeQuery("BEGIN PROCEDURE_NAME(); END;");
query.executeUpdate();
Run Code Online (Sandbox Code Playgroud)

带花括号的变体导致ORA-00900.

  • 谢谢!这对我来说真的很有效(Tomcat + Oracle11g) (2认同)

Jam*_*mes 6

如果使用EclipseLink,您可以使用@NamedStoredProcedureQuery或StoreProcedureCall来执行任何存储过程,包括具有输出参数或存储游标的存储过程.还支持存储函数和PLSQL数据类型.

请参阅 http://en.wikibooks.org/wiki/Java_Persistence/Advanced_Topics#Stored_Procedures


Vla*_*cea 5

  1. 对于使用这样的IN / OUT参数的简单存储过程

    CREATE OR REPLACE PROCEDURE count_comments (  
       postId IN NUMBER,  
       commentCount OUT NUMBER )  
    AS 
    BEGIN 
        SELECT COUNT(*) INTO commentCount  
        FROM post_comment  
        WHERE post_id = postId; 
    END;
    
    Run Code Online (Sandbox Code Playgroud)

    您可以从JPA调用它,如下所示:

    StoredProcedureQuery query = entityManager
        .createStoredProcedureQuery("count_comments")
        .registerStoredProcedureParameter(1, Long.class, 
            ParameterMode.IN)
        .registerStoredProcedureParameter(2, Long.class, 
            ParameterMode.OUT)
        .setParameter(1, 1L);
    
    query.execute();
    
    Long commentCount = (Long) query.getOutputParameterValue(2);
    
    Run Code Online (Sandbox Code Playgroud)
  2. 对于使用SYS_REFCURSOROUT参数的存储过程:

    CREATE OR REPLACE PROCEDURE post_comments ( 
       postId IN NUMBER, 
       postComments OUT SYS_REFCURSOR ) 
    AS 
    BEGIN
        OPEN postComments FOR
        SELECT *
        FROM post_comment 
        WHERE post_id = postId; 
    END;
    
    Run Code Online (Sandbox Code Playgroud)

    您可以按以下方式调用它:

    StoredProcedureQuery query = entityManager
        .createStoredProcedureQuery("post_comments")
        .registerStoredProcedureParameter(1, Long.class, 
             ParameterMode.IN)
        .registerStoredProcedureParameter(2, Class.class, 
             ParameterMode.REF_CURSOR)
        .setParameter(1, 1L);
    
    query.execute();
    
    List<Object[]> postComments = query.getResultList();
    
    Run Code Online (Sandbox Code Playgroud)
  3. 对于看起来如下的SQL函数:

    CREATE OR REPLACE FUNCTION fn_count_comments ( 
        postId IN NUMBER ) 
        RETURN NUMBER 
    IS
        commentCount NUMBER; 
    BEGIN
        SELECT COUNT(*) INTO commentCount 
        FROM post_comment 
        WHERE post_id = postId; 
        RETURN( commentCount ); 
    END;
    
    Run Code Online (Sandbox Code Playgroud)

    您可以这样称呼它:

    BigDecimal commentCount = (BigDecimal) entityManager
    .createNativeQuery(
        "SELECT fn_count_comments(:postId) FROM DUAL"
    )
    .setParameter("postId", 1L)
    .getSingleResult();
    
    Run Code Online (Sandbox Code Playgroud)

    至少在使用Hibernate 4.x和5.x时,因为JPA StoredProcedureQuery不适用于SQL FUNCTIONS。

有关使用JPA和Hibernate时如何调用存储过程和函数的更多详细信息,请查看以下文章。


vin*_*oth 0

要调用存储过程,我们可以使用java.sql包中的Callable Statement。