【问题标题】:Hibernate: how to call a stored function returning a varchar?Hibernate:如何调用返回 varchar 的存储函数?
【发布时间】:2011-02-25 22:39:51
【问题描述】:

我正在尝试使用 Hibernate 从 Java 调用 Oracle9i DB 中的旧存储函数。函数声明如下:

create or replace FUNCTION Transferlocation_Fix (mnemonic_code IN VARCHAR2)
   RETURN VARCHAR2

在几次失败的尝试和广泛的谷歌搜索之后,我在 Hibernate 论坛上找到了this thread,它提出了这样的映射:

<sql-query name="TransferLocationFix" callable="true">
    <return-scalar column="retVal" type="string"/>
    select Transferlocation_Fix(:mnemonic) as retVal from dual
</sql-query>

我的执行代码是

    Query query = session.getNamedQuery("TransferLocationFix");
    query.setParameter("mnemonic", "FC3");
    String result = (String) query.uniqueResult();

结果日志是

DEBUG (org.hibernate.jdbc.AbstractBatcher:366) -  - about to open PreparedStatement (open PreparedStatements: 0, globally: 0)
DEBUG (org.hibernate.SQL:401) -  - select Transferlocation_Fix(?) as retVal from dual
TRACE (org.hibernate.jdbc.AbstractBatcher:484) -  - preparing statement
TRACE (org.hibernate.type.StringType:133) -  - binding 'FC3' to parameter: 2
TRACE (org.hibernate.type.StringType:133) -  - binding 'FC3' to parameter: 2

java.lang.NullPointerException
at oracle.jdbc.ttc7.TTCAdapter.newTTCType(TTCAdapter.java:300)
at oracle.jdbc.ttc7.TTCAdapter.createNonPlsqlTTCColumnArray(TTCAdapter.java:270)
at oracle.jdbc.ttc7.TTCAdapter.createNonPlsqlTTCDataSet(TTCAdapter.java:231)
at oracle.jdbc.ttc7.TTC7Protocol.doOall7(TTC7Protocol.java:1924)
at oracle.jdbc.ttc7.TTC7Protocol.parseExecuteDescribe(TTC7Protocol.java:850)
at oracle.jdbc.driver.OracleStatement.doExecuteQuery(OracleStatement.java:2599)
at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java:2963)
at oracle.jdbc.driver.OraclePreparedStatement.executeUpdate(OraclePreparedStatement.java:658)
at oracle.jdbc.driver.OraclePreparedStatement.execute(OraclePreparedStatement.java:736)
at com.mchange.v2.c3p0.impl.NewProxyCallableStatement.execute(NewProxyCallableStatement.java:3044)
at org.hibernate.dialect.Oracle8iDialect.getResultSet(Oracle8iDialect.java:379)
at org.hibernate.jdbc.AbstractBatcher.getResultSet(AbstractBatcher.java:193)
at org.hibernate.loader.Loader.getResultSet(Loader.java:1784)
at org.hibernate.loader.Loader.doQuery(Loader.java:674)
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:236)
at org.hibernate.loader.Loader.doList(Loader.java:2220)
at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2104)
at org.hibernate.loader.Loader.list(Loader.java:2099)
at org.hibernate.loader.custom.CustomLoader.list(CustomLoader.java:289)
at org.hibernate.impl.SessionImpl.listCustomQuery(SessionImpl.java:1695)
at org.hibernate.impl.AbstractSessionImpl.list(AbstractSessionImpl.java:142)
at org.hibernate.impl.SQLQueryImpl.list(SQLQueryImpl.java:152)
at org.hibernate.impl.AbstractQueryImpl.uniqueResult(AbstractQueryImpl.java:811)
at com.my.project.SomeClass.method(SomeClass.java:202)
...

任何线索我做错了什么?或者有什么更好的方法来调用这个存储函数?


更新:在尝试@axtavt 的建议时,我收到以下错误:

ORA-14551: cannot perform a DML operation inside a query

该函数确实进行了大量的插入/更新,所以我想运行它的唯一方法是使用存储过程语法。我只是不知道如何映射返回值:

<sql-query name="TransferLocationFix" callable="true">
    <return-scalar column="???" type="string"/>
    { ? = call Transferlocation_Fix(:mnemonic) }
</sql-query>

column 应该是什么?我会尝试一个空值...


Update2: 也失败了,带有 SQL 语法异常...所以我尝试了 Pascal 建议的 JDBC 方式,它似乎可以工作!我在下面的答案中添加了代码。

【问题讨论】:

    标签: java oracle hibernate stored-procedures


    【解决方案1】:

    为了进一步参考,这是我的最终解决方案:

    CallableStatement statement = session.connection().prepareCall(
            "{ ? = call Transferlocation_Fix(?) }");
    statement.registerOutParameter(1, Types.VARCHAR);
    statement.setString(2, "FC3");
    statement.execute();
    String result = statement.getString(1);
    

    【讨论】:

    • 但是如何使用 javax.persistence.EntityManager ..?
    • @kanagavelu-sugumar =你收到答复了吗
    【解决方案2】:

    我不是 100% 确定,我没有测试它,但根据 Hibernate 的文档:

    16.2.2. Using stored procedures for querying

    Hibernate3 支持 通过存储过程查询和 职能。以下大部分 两者的文档是等效的。 存储过程/函数必须返回一个结果集作为第一个 能够使用的输出参数 休眠。这样的一个例子 Oracle 9 及更高版本中的存储函数 如下:

    CREATE OR REPLACE FUNCTION selectAllEmployments
        RETURN SYS_REFCURSOR
    AS
        st_cursor SYS_REFCURSOR;
    BEGIN
        OPEN st_cursor FOR
     SELECT EMPLOYEE, EMPLOYER,
     STARTDATE, ENDDATE,
     REGIONCODE, EID, VALUE, CURRENCY
     FROM EMPLOYMENT;
          RETURN  st_cursor;
     END;
    

    要在 Hibernate 中使用此查询,您 需要通过命名查询对其进行映射。

    <sql-query name="selectAllEmployees_SP" callable="true">
        <return alias="emp" class="Employment">
            <return-property name="employee" column="EMPLOYEE"/>
            <return-property name="employer" column="EMPLOYER"/>
            <return-property name="startDate" column="STARTDATE"/>
            <return-property name="endDate" column="ENDDATE"/>
            <return-property name="regionCode" column="REGIONCODE"/>
            <return-property name="id" column="EID"/>
            <return-property name="salary">
                <return-column name="VALUE"/>
                <return-column name="CURRENCY"/>
            </return-property>
        </return>
        { ? = call selectAllEmployments() }
    </sql-query>
    

    目前只有存储过程 返回标量和实体。 &lt;return-join&gt; 和 不支持&lt;load-collection&gt;

    16.2.2.1. Rules/limitations for using stored procedures

    您不能将存储过程与 休眠,除非你遵循一些 程序/功能规则。 如果他们这样做 不遵守那些他们不遵守的规则 可用于休眠。如果你还 想要使用你拥有的这些程序 通过执行它们 session.connection()。规则是 每个数据库都不同,因为 数据库供应商有不同的存储 过程语义/语法。

    存储过程查询不能 与 setFirstResult()/setMaxResults().

    推荐的调用形式是标准的 SQL92:{ ? = call functionName(<parameters>) }{ ? = call procedureName(<parameters>}。不支持本机调用语法。

    对于 Oracle,适用以下规则:

    • 函数必须返回结果集。 a的第一个参数 过程必须是返回的 OUT 结果集。这是通过使用 Oracle 9 中的 SYS_REFCURSOR 类型或 10. 在Oracle 中你需要定义一个REF CURSOR 类型。参见 Oracle 文献 了解更多信息。

    ...

    正如我所说,我不确定,但我的理解是你必须在这里使用session.getConnection()

    【讨论】:

    • 我也读过,但我也不确定,因为在本书的不同部分(第 8.2.2 章)中有一节关于调用存储函数(但是,没有简单的示例:-( )。我发现的论坛主题表明确实可以使用getNamedQuery()...
    • @Péter 确实,axtavt 的回答也朝着同一个方向发展。我很想看看它是否有效。
    • 仅供参考,最后证明 Hibernate 文档在这种情况下是正确的,所以我不得不使用 JDBC。
    • @Péter 感谢您的反馈。
    【解决方案3】:

    callable = true 用于使用{? = call ...()} 语法调用存储过程。 Oracle的select ... from dual语法是普通查询,所以不需要callable = true

    <sql-query name="TransferLocationFix"> 
        <return-scalar column="retVal" type="string"/> 
        select Transferlocation_Fix(:mnemonic) as retVal from dual 
    </sql-query> 
    

    【讨论】:

      【解决方案4】:

      我遇到了类似的问题/问题,我开始意识到应该在 sql 部分进行更改,因为 hibernate 仅适用于游标返回。我已经在这里描述了一切:http://www.len.ro/2011/10/call-oracle-procedure-from-hibernate/

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-12-19
        • 1970-01-01
        • 1970-01-01
        • 2011-12-19
        • 1970-01-01
        • 2018-04-23
        相关资源
        最近更新 更多