我试图使用Hibernate从Java调用Oracle9i数据库中的遗留存储函数。该函数声明如下:
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方式,它似乎工作了!我在下面的答案中添加了代码。
答案 0 :(得分: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);
答案 1 :(得分:2)
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>
答案 2 :(得分:2)
我不是100%肯定而且我没有测试它,但根据Hibernate的文档:
16.2.2. Using stored procedures for querying
Hibernate3提供支持 通过存储过程查询和 功能。大部分以下内容 文档对两者都是等价的。 存储过程/函数必须返回结果集作为第一个 out-parameter能够使用 冬眠即可。这样一个例子 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>
和 1}}不受支持。16.2.2.1. Rules/limitations for using stored procedures
您无法使用存储过程 Hibernate,除非你遵循一些 程序/功能规则。 如果有的话 不遵循他们不遵守的规则 可用于Hibernate。如果你还是 想要使用这些程序 通过执行它们
<load-collection>
即可。规则是 因为每个数据库都不同 数据库供应商有不同的存储 过程语义/语法。存储过程查询不能 分页
session.connection()
。推荐的致电表格是标准的 SQL92:
setFirstResult()/setMaxResults()
或{ ? = call functionName(<parameters>) }
。不支持本机调用语法。对于Oracle,以下规则适用:
- 函数必须返回结果集。 a的第一个参数 程序必须是返回的OUT 结果集。这是通过使用a来完成的 Oracle 9或中的SYS_REFCURSOR类型 10。在Oracle中,您需要定义REF CURSOR类型。请参阅Oracle文献 了解更多信息。
...
正如我所说,我不确定,但我的理解是你必须在这里使用{ ?
= call procedureName(<parameters>}
。
答案 3 :(得分:2)
我遇到了类似的问题/问题我已经意识到应该在sql部分进行更改,因为hibernate只能使用游标返回。我在这里描述了一切:http://www.len.ro/2011/10/call-oracle-procedure-from-hibernate/