我试图使用Hibernate从Java调用Oracle9i DB中的遗留存储函数。该函数的声明如下:
create or replace FUNCTION Transferlocation_Fix (mnemonic_code IN VARCHAR2) RETURN VARCHAR2
经过几次失败的尝试和广泛的谷歌搜索之后,我在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?我会尝试一个空值…
column
Update2: 同样失败,并带有SQL语法异常…因此,我尝试了Pascal建议的JDBC方式,它似乎可以工作!我在下面的答案中添加了代码。
我不确定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; 要在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> 当前,存储过程仅返回标量和实体。 <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文献。 …
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;
要在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>
当前,存储过程仅返回标量和实体。 <return-join>并且 <load-collection>不受支持。
<return-join>
<load-collection>
16.2.2.1。使用存储过程的规则/限制
除非遵循某些过程/功能规则,否则您不能在Hibernate中使用存储过程。 如果他们不遵守这些规则,则无法在Hibernate中使用。 如果仍然要使用这些过程,则必须通过来执行它们 session.connection()。每个数据库的规则都不同,因为数据库供应商具有不同的存储过程语义/语法。
session.connection()
存储过程查询不能使用进行分页 setFirstResult()/setMaxResults()。
setFirstResult()/setMaxResults()
推荐的调用形式为标准SQL92:{ ? = call functionName(<parameters>) }或{ ? = call procedureName(<parameters>}。不支持本机调用语法。
{ ? = call functionName(<parameters>) }
{ ? = call procedureName(<parameters>}
对于Oracle,适用以下规则:
…
如我所说,我不确定,但我的理解是您必须session.getConnection()在这里使用。
session.getConnection()