Estoy tratando de llamar a un legado función almacenada en un Oracle9i DB de Java con Hibernate. La función se declara como este:

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

Después de varios intentos fallidos y extensa buscando en google, he encontrado este hilo en la Hibernación de los foros que sugiere una asignación como este:

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

Mi código a ejecutar es

    Query query = session.getNamedQuery("TransferLocationFix");
    query.setParameter("mnemonic", "FC3");
    String result = (String) query.uniqueResult();

y el registro resultante es

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

Alguna pista de lo que estoy haciendo mal? O alguna forma mejor para llamar a esta función almacenada?


Actualización: tratando de @axtavt la sugerencia, me sale el siguiente error:

ORA-14551: cannot perform a DML operation inside a query

La función, de hecho, hace muchos inserciones y actualizaciones, así que supongo que la única forma de ejecutar sería el uso de la sintaxis del procedimiento almacenado. Sólo tengo ni idea de cómo asignar el valor de retorno:

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

Lo que debería ser el column? Voy a tratar un valor vacío…


Update2: que no se pudo, así, con una Gramática SQL Excepción… Así que traté de JDBC manera como se sugiere por Pascal, y parece que funciona! He añadido el código en una respuesta a continuación.

OriginalEl autor Péter Török | 2010-05-31

4 Comentarios

  1. 2

    No estoy 100% seguro y no llegué a probarlo, pero de acuerdo a la documentación de Hibernate:

    16.2.2. El uso de procedimientos almacenados para su consulta

    Hibernate3 proporciona soporte para
    consultas a través de procedimientos almacenados y
    funciones. La mayoría de los siguientes
    la documentación es igual para ambos.
    El procedimiento almacenado/función debe devolver un conjunto de resultados como la primera
    parámetro de salida para ser capaz de trabajar con
    Hibernate
    . Un ejemplo de este tipo de
    función almacenada en Oracle 9 y superiores
    es como sigue:

    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;

    Para utilizar esta consulta en Hibernate
    necesidad de hacer un mapa a través de una consulta con nombre.

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

    Procedimientos almacenados actualmente sólo
    volver escalares y entidades.
    <return-join> y
    <load-collection> no son compatibles.

    16.2.2.1. Reglas y limitaciones para el uso de procedimientos almacenados

    No puede utilizar procedimientos almacenados con
    Hibernate, debe seguir algunos
    procedimiento/función de reglas. Si lo hacen
    no seguir estas normas no son
    utilizable con Hibernate. Si usted todavía
    desea utilizar estos procedimientos tiene
    para ejecutarlas a través de
    session.connection()
    . Las reglas son
    diferentes para cada base de datos, ya que
    proveedores de bases de datos tienen diferentes almacenado
    procedimiento sintaxis/semántica.

    Procedimiento almacenado de consultas no puede ser
    paginada con
    setFirstResult()/setMaxResults().

    La llamada recomendada formulario estándar
    SQL92: { ? = call
    functionName(<parameters>) }
    o { ?
    = call procedureName(<parameters>}
    . Nativo de la sintaxis de la llamada no es compatible.

    Para Oracle se aplican las siguientes reglas:

    • Una función debe devolver un conjunto de resultados. El primer parámetro de un
      el procedimiento debe ser una que devuelve
      un conjunto de resultados. Esto se hace mediante el uso de un
      Tipo SYS_REFCURSOR en Oracle 9 o
      10.
      En Oracle necesita definir un tipo REF CURSOR. Consulte la documentación de Oracle
      para obtener más información.

    Como ya he dicho, no estoy seguro, pero mi entendimiento es que usted tendrá que usar session.getConnection() aquí.

    He leído que también, pero no estoy seguro, ya que en una parte diferente del libro (ch 8.2.2) hay una sección acerca de llamada almacenados funciones (sin embargo, no es sencillo ejemplo 🙁 ). Y el hilo del foro he encontrado sugiere que sí es posible el uso de getNamedQuery()
    De hecho, y axtavt la respuesta va en la misma dirección. Estoy ansioso de ver si funciona.
    FYI, al final resultó que, en la documentación de Hibernate es correcto en este caso, por lo que tuve que usar JDBC.
    Gracias por los comentarios.

    OriginalEl autor Pascal Thivent

  2. 10

    Para mayor referencia, aquí está mi solución final:

    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 para la publicación de la solución real
    Pero ¿cómo hacerlo con javax.la persistencia.EntityManager ..?
    usted obtener la answere plese respuesta

    OriginalEl autor Péter Török

  3. 2

    callable = true es para llamar a procedimientos almacenados con {? = call ...()} sintaxis. Oracle select ... from dual sintaxis es normal en la consulta, por lo que no necesita callable = true:

    <sql-query name="TransferLocationFix"> 
    <return-scalar column="retVal" type="string"/> 
    select Transferlocation_Fix(:mnemonic) as retVal from dual 
    </sql-query> 
    Ahhh, muy interesante. Yo no era consciente de que (si funciona).
    He probado tu sugerencia, favor de ver mis actualizaciones.

    OriginalEl autor axtavt

Dejar respuesta

Please enter your comment!
Please enter your name here