2010-05-31 6 views
5

Я пытаюсь вызвать унаследованную сохраненную функцию в Oracle9i DB с Java, используя Hibernate. Функция объявлена ​​так:Hibernate: как вызвать сохраненную функцию, возвращающую varchar?

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

Любые подсказки, что я делаю неправильно? Или какие-либо лучшие способы вызова этой хранимой функции?


Update: при попытке @ предложение 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 Grammar ... Так что я попытался, как JDBC, как предложил Паскаль, и это, кажется, работает! Я добавил код в ответ ниже.

ответ

2

Я не уверен на 100%, и я не проверял, но в соответствии с документацией в Hibernate:

16.2.2. Using stored procedures for querying

Hibernate3 provides support for queries via stored procedures and functions. Most of the following documentation is equivalent for both. The stored procedure/function must return a resultset as the first out-parameter to be able to work with Hibernate. An example of such a stored function in Oracle 9 and higher is as follows:

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; 

To use this query in Hibernate you need to map it via a named query.

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

Stored procedures currently only return scalars and entities. <return-join> and <load-collection> are not supported.

16.2.2.1. Rules/limitations for using stored procedures

You cannot use stored procedures with Hibernate unless you follow some procedure/function rules. If they do not follow those rules they are not usable with Hibernate. If you still want to use these procedures you have to execute them via session.connection(). The rules are different for each database, since database vendors have different stored procedure semantics/syntax.

Stored procedure queries cannot be paged with setFirstResult()/setMaxResults() .

The recommended call form is standard SQL92: { ? = call functionName(<parameters>) } or { ? = call procedureName(<parameters>} . Native call syntax is not supported.

For Oracle the following rules apply:

  • A function must return a result set. The first parameter of a procedure must be an OUT that returns a result set. This is done by using a SYS_REFCURSOR type in Oracle 9 or 10. In Oracle you need to define a REF CURSOR type. See Oracle literature for further information.

...

As Я сказал, я не уверен, но я понимаю, что вам нужно будет использовать session.getConnection().

+0

Я тоже это читал, но я тоже не уверен, поскольку в другой части книги (глава 8.2.2) есть раздел о вызове хранимых функций (однако нет простого примера :-(). я нашел вывод, что это действительно возможно с помощью 'getNamedQuery()' ... –

+0

@ Péter В самом деле, и ответ axtavt идет в том же направлении. Я очень хочу посмотреть, работает ли это. –

+0

FYI, в конце концов, оказалось, что в этом случае документация Hibernate подходит, поэтому мне пришлось использовать JDBC. –

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

Ahhh, интересно. Я не знал об этом (если это работает). –

+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); 
+0

+1 для публикации фактического решения –

2

Я столкнулся с подобным вопросом/проблемой, и я пришел к выводу, изменениям должны быть сделан на SQL части как Hibernate работы только с возвратом курсора. Я описал все здесь: http://www.len.ro/2011/10/call-oracle-procedure-from-hibernate/

Смежные вопросы