如何通过 Java EntityManager 调用 PL/SQL 存储函数

Posted

技术标签:

【中文标题】如何通过 Java EntityManager 调用 PL/SQL 存储函数【英文标题】:How to call a PL/SQL stored function through Java EntityManager 【发布时间】:2014-11-25 15:35:20 【问题描述】:

我有这个功能

create or replace
FUNCTION IFRS_FUNCTION_TEST RETURN NUMBER IS

estado NUMBER;
excepciones NUMBER;
title_code VARCHAR2(150 char);
colgaap_code VARCHAR2(150 char);
ifrs_code VARCHAR2(150 char);
v_causacion VARCHAR2(150 char);
pyg_diario_value VARCHAR2(150 char);
fecha_valoracion VARCHAR2(150 char);
pyg_dialogo VARCHAR2(150 char);

CURSOR ifrscursor IS SELECT no_asignado_por_la_entidad, codigo_puc FROM IFRS_351 WHERE no_asignado_por_la_entidad LIKE 'W%' FOR UPDATE;

BEGIN
        BEGIN
        estado:=0;
        excepciones:=0;
        OPEN ifrscursor;
        LOOP
            FETCH ifrscursor INTO title_code, colgaap_code; 
            EXIT WHEN ifrscursor%NOTFOUND;
            SELECT cta_ifrs into ifrs_code FROM ifrs_tabla_homo_351 WHERE codigo_puc_colgaap = colgaap_code;
            UPDATE IFRS_351 SET codigo_puc = ifrs_code WHERE CURRENT OF ifrscursor;
            BEGIN
                SELECT saldo, pyg_diario into v_causacion, pyg_diario_value FROM IFRS_INV_OBL WHERE nro_titulo=title_code;
                UPDATE IFRS_351 SET vr_mercado_o_valor_presente_ = v_causacion, causacion_valoracion_cuentas = pyg_diario_value WHERE CURRENT OF ifrscursor;
            EXCEPTION WHEN NO_DATA_FOUND THEN
                excepciones:=excepciones+1;
            END;
END LOOP; 
CLOSE ifrscursor;
EXCEPTION WHEN OTHERS THEN
    estado:=SQLCODE;  
END;
RETURN(estado);

如何使用 Java EntityManager 获取此函数的 estado 值?我试过了

SELECT IFRS_FUNCTION_TEST() FROM DUAL;

但我总是得到错误代码 14551。

【问题讨论】:

【参考方案1】:

值 -14551 实际上是 Oracle 错误而不是值。该错误在下面详细说明,应该详细说明它为什么不工作。

ORA-14551 无法在查询中执行 DML 操作原因:DML 不能进行诸如插入、更新、删除或选择更新之类的操作 在查询内部或在 PDML 从属设备下执行。行动:确保 有问题的 DML 操作未执行或使用自治 在查询或 PDML 中执行 DML 操作的事务 奴隶。

【讨论】:

【参考方案2】:

您不能使用SELECT 查询来检索包含 DML 语句的函数的值。您必须使用显式调用。 类似的东西(未经测试):

Object result = em.createNativeQuery(" ? = CALL IFRS_FUNCTION_TEST() ")
                    .getSingleResult();

编辑:因为这似乎 工作,另一种选择是直接通过 JDBC 发送该查询(通过session.getConnection())。 类似的东西可能:

CallableStatement statement = session.connection().prepareCall(
        " ? = CALL IFRS_FUNCTION_TEST() ");
statement.registerOutParameter(1, Types.OTHER);
//                                      ^^^^^
//                       use the right type here of course
statement.execute();
Object result = statement.getObject(1);

详见OracleCallableStatement的文档

【讨论】:

如果我使用 createNativeQuery,我会得到一个异常,因为该方法是一个函数,而不是一个过程 @user3476922 抱歉,我现在无法测试。请尝试"CALL IFRS_FUNCTION_TEST()""BEGIN ? := IFRS_FUNCTION_TEST(); END" 结果是一样的。感谢您的关注! @user3476922 那个? " ? = CALL IFRS_FUNCTION_TEST() "同样的错误信息? @user3476922 FWIW,我已经编辑了我的答案以提供另一个选项,使用普通的旧 JDBC。【参考方案3】:

JPA 2.0 不支持 RETURN 值,只支持调用。 JPA 2.1 修复了这个问题,

如果您使用的是 JPA 2.0。 我的解决方案是。创建一个调用 PROCEDURE 的函数。 因此,在 JAVA 代码中,您执行一个调用 oracle FUNCTION 的 NATIVE QUERY。

否则 使用 createStoredProcedureQuery

【讨论】:

【参考方案4】:

这是一个使用javax.persistence.EntityManager的解决方案:

@PersistenceContext
private EntityManager entityManager;

. . .

int estado;
Session session = entityManager.unwrap(Session.class);
session.doWork(connection -> 
    try (CallableStatement function = connection.prepareCall(
            " ? = call IFRS_FUNCTION_TEST() " )) 
        function.registerOutParameter( 1, Types.INTEGER );
        function.execute();
        estado = function.getInt(1);
    
 );

这些是指示解决方案适用于哪些 lib 版本的主要依赖项:

compile 'com.oracle:ojdbc6:11.2.0.4.0'
compile 'org.hibernate:hibernate-core:5.4.7.Final'
compile 'org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.2.Final'

作为doWork() 的替代方法并将存储的函数结果分配给getXxx(1),您可以使用doReturningWork()

正如其他人指出的那样,由于函数内部有 UPDATE (DML) 命令,您不能使用(本机)查询。

在 Kotlin 中

这是 Kotlin 中的解决方案(并使用 doReturningWork):

@PersistenceContext
lateinit var entityManager: EntityManager

. . .

val session : Session = entityManager.unwrap(Session::class.java)
var estado = session.doReturningWork  connection: Connection ->
    connection.prepareCall(
            " ? = call IFRS_FUNCTION_TEST() ").use  function ->
        function.registerOutParameter(1, Types.INTEGER)
        function.execute()
        return@doReturningWork function.getInt(1)
    

【讨论】:

以上是关于如何通过 Java EntityManager 调用 PL/SQL 存储函数的主要内容,如果未能解决你的问题,请参考以下文章

如何使用休眠将mysql与Java连接?例外:没有名为 org.hibernate.tutorial_jpa 的 EntityManager 的持久性提供程序 [关闭]

Eclipselink:如何在每个捆绑包中获取 EntityManager?

jdbcTemplate 更新后如何刷新 entityManager 状态

为我的 EntityManager 获取空指针异常

如何使用 Doctrine QueryBuilder 或 EntityManager 通过多对多相关实体查找实体

JTA EntityManager不能在存储过程调用中使用getTransaction()