Hibernate: как вызвать хранимую функцию, возвращающую varchar? - PullRequest
5 голосов
/ 31 мая 2010

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

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

После нескольких неудачных попыток и интенсивного поиска в Google я нашел эту ветку на форумах 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? Я попробую пустое значение ...


Update2: , который также не удалось, с исключением грамматики SQL ... Итак, я попробовал способ JDBC, как предложил Паскаль, и, похоже, он работает! Я добавил код в ответе ниже.

Ответы [ 4 ]

10 голосов
/ 01 июня 2010

Для дальнейшего ознакомления, вот мое окончательное решение:

CallableStatement statement = session.connection().prepareCall(
        "{ ? = call Transferlocation_Fix(?) }");
statement.registerOutParameter(1, Types.VARCHAR);
statement.setString(2, "FC3");
statement.execute();
String result = statement.getString(1);
2 голосов
/ 18 октября 2011

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

2 голосов
/ 31 мая 2010

Я не уверен на 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, которая возвращает набор результатов. Это делается с помощью Тип SYS_REFCURSOR в Oracle 9 или 10. В Oracle вам нужно определить тип REF CURSOR. Смотрите литературу по Oracle для дальнейшей информации.

...

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

2 голосов
/ 31 мая 2010

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> 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...