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

Pét*_*rök 5 java oracle stored-procedures hibernate

我试图使用Hibernate从Java调用Oracle9i数据库中的遗留存储函数.该函数声明如下:

create or replace FUNCTION Transferlocation_Fix (mnemonic_code IN VARCHAR2)
   RETURN VARCHAR2
Run Code Online (Sandbox Code Playgroud)

经过几次失败的尝试和广泛的谷歌搜索后,我在Hibernate论坛上发现了这个线程,它提出了这样的映射:

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

我执行它的代码是

    Query query = session.getNamedQuery("TransferLocationFix");
    query.setParameter("mnemonic", "FC3");
    String result = (String) query.uniqueResult();
Run Code Online (Sandbox Code Playgroud)

结果日志是

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)
...
Run Code Online (Sandbox Code Playgroud)

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


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

ORA-14551: cannot perform a DML operation inside a query
Run Code Online (Sandbox Code Playgroud)

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

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

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


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

Pét*_*rök 10

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

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


Pas*_*ent 2

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

16.2.2. 使用存储过程进行查询

Hibernate3 通过存储过程和函数提供对查询的支持。以下大部分文档对于两者来说都是等效的。 存储过程/函数必须返回结果集作为第一个输出参数才能与 Hibernate 一起使用。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;
Run Code Online (Sandbox Code Playgroud)

要在 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>
Run Code Online (Sandbox Code Playgroud)

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

16.2.2.1。使用存储过程的规则/限制

除非遵循某些过程/函数规则,否则不能将存储过程与 Hibernate 一起使用。如果它们不遵循这些规则,则它们无法与 Hibernate 一起使用。如果您仍然想使用这些过程,则必须通过 执行它们 session.connection()。每个数据库的规则都不同,因为数据库供应商具有不同的存储过程语义/语法。

存储过程查询不能使用 进行分页 setFirstResult()/setMaxResults()

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

对于 Oracle,适用以下规则:

  • 函数必须返回结果集。过程的第一个参数必须是返回结果集的 OUT。这是通过在 Oracle 9 或 10 中使用 SYS_REFCURSOR 类型来完成的。在 Oracle 中,您需要定义 REF CURSOR 类型。有关更多信息,请参阅 Oracle 文献。

...

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