2010-05-31 4 views
5

Ich versuche, eine ältere gespeicherte Funktion in einer Oracle9i DB von Java mit Hibernate aufzurufen. Die Funktion wird wie folgt erklärt:Hibernate: Wie eine gespeicherte Funktion aufgerufen wird, die ein Varchar zurückgibt?

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

Nach mehreren gescheiterten Versuchen und umfangreiche googeln, ich this thread auf den Hibernate-Foren gefunden, die eine Zuordnung wie folgt vorgeschlagen:

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

Mein Code auszuführen ist

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

und das resultierende Protokoll ist

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

Irgendwelche Hinweise was mache ich falsch? Oder gibt es bessere Möglichkeiten, diese gespeicherte Funktion aufzurufen?


Update: auf @ axtavt Vorschlag versucht, bekomme ich folgende Fehlermeldung:

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

Die Funktion hat in der Tat umfangreiche Einsätze/Updates, so dass ich denke, der einzige Weg, um es auszuführen wäre Verwenden der Syntax der gespeicherten Prozedur. Ich habe keine Ahnung, wie der Rückgabewert auf der Karte:

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

Was sollte die column sein? Ich werde einen leeren Wert versuchen ...


Update2:, die als gut ausgefallen, mit einem SQL-Grammatik Exception ... Also habe ich versucht, den JDBC-Weg, wie Pascal vorgeschlagen, und es scheint zu funktionieren! Ich habe den Code in einer Antwort unten hinzugefügt.

Antwort

2

Ich bin nicht 100% sicher, und ich habe es nicht testen, aber nach Hibernate Dokumentation:

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.

...

Wie Ich sagte, ich bin mir nicht sicher, aber mein Verständnis ist, dass Sie session.getConnection() hier verwenden müssen.

+0

Ich lese das auch, aber ich bin mir auch nicht sicher, da es in einem anderen Teil des Buches (Kap. 8.2.2) einen Abschnitt über den Aufruf gespeicherter Funktionen gibt (allerdings gibt es kein einfaches Beispiel :-() Forum-Thread, den ich gefunden habe, legt nahe, dass es tatsächlich möglich ist, 'getNamedQuery()' ... zu verwenden. –

+0

@ Péter In der Tat, und axtavts Antwort geht in die gleiche Richtung. Ich bin gespannt, ob es funktioniert. –

+0

FYI, am Ende stellte sich heraus, dass die Hibernate-Dokumentation in diesem Fall richtig war, also musste ich JDBC verwenden. –

2

callable = true ist für den Aufruf gespeicherter Prozeduren mit {? = call ...()} Syntax. Oracle select ... from dual Syntax ist eine normale Abfrage, so dass Sie callable = true nicht brauchen:

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

Ahhh, interessant. Ich war mir dessen nicht bewusst (wenn das funktioniert). –

+0

Ich habe Ihren Vorschlag ausprobiert, bitte sehen Sie mein Update. –

10

Für weitere Referenz, hier ist meine letzte Lösung:

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 für die Buchung der tatsächlichen Lösung –

Verwandte Themen