代码之家  ›  专栏  ›  技术社区  ›  Péter Török

休眠:如何调用返回varchar的存储函数?

  •  5
  • Péter Török  · 技术社区  · 15 年前

    我试图用Hibernate从Java中调用Oracle 9i数据库中的遗留存储函数。函数的声明如下:

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

    在几次失败的尝试和大量的谷歌搜索之后,我发现 this thread 在Hibernate论坛上建议这样的映射:

    <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 ?我将尝试一个空值…


    更新2: 也失败了,出现了SQL语法异常…所以我按照帕斯卡的建议尝试了JDBC的方法,但它似乎奏效了!我在下面的答案中添加了代码。

    4 回复  |  直到 9 年前
        1
  •  2
  •   Pascal Thivent    15 年前

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

    16.2.2. Using stored procedures for querying

    Hibernate3支持 通过存储过程和 功能。以下大部分 文档对两者都是等效的。 存储过程/函数必须首先返回结果集 可以使用的out参数 冬眠 . 这样一个例子 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;
    

    要在休眠中使用此查询,请 需要通过命名查询映射它。

    <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>
    

    当前仅存储过程 返回标量和实体。 <return-join> <load-collection> 不支持。

    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或 10。 在Oracle中,您需要定义一个引用光标类型。见甲骨文 更多信息。

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

        2
  •  10
  •   Péter Török    15 年前

    以下是我的最终解决方案,供进一步参考:

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

    callable = true 用于调用存储过程 {? = call ...()} 语法。甲骨文公司 select ... from dual 语法是一个普通的查询,因此您不需要 可调用=真 :

    <sql-query name="TransferLocationFix"> 
        <return-scalar column="retVal" type="string"/> 
        select Transferlocation_Fix(:mnemonic) as retVal from dual 
    </sql-query> 
    
        4
  •  2
  •   len    13 年前

    我遇到了一个类似的问题,我开始意识到应该在SQL部分进行更改,因为Hibernate只处理光标返回。我在这里描述了一切: http://www.len.ro/2011/10/call-oracle-procedure-from-hibernate/