2009-11-09 3 views
14

La mia domanda è molto simile a Getting the return value of a PL/SQL function via HibernateCome chiamare una funzione Oracle da Hibernate con un parametro di ritorno?

Ho una funzione che esegue alcune modifiche internamente e restituisce un valore.

L'idea originale era quella di fare qualcosa di simile:

protected Integer checkXXX(Long id, Long transId) 
     throws Exception { 
    final String sql = "SELECT MYSCHEMA.MYFUNC(" + id + ", " 
      + transId + ") FROM DUAL"; 
    final BigDecimal nr = (BigDecimal) this.getHibernateTemplate() 
      .getSessionFactory().getCurrentSession().createSQLQuery(sql) 
      .uniqueResult(); 
    return nr.intValue(); 
} 

Purtroppo questo non funziona con Oracle. Qual è il modo consigliato per fare qualcosa di simile?

Esiste un modo per estrarre le variabili dichiarate dall'interno della mia istruzione?

risposta

29

Hibernate Session fornisce un metodo doWork() che consente l'accesso diretto a java.sql.Connection. È quindi possibile creare e utilizzare java.sql.CallableStatement per eseguire la funzione:

session.doWork(new Work() { 
    public void execute(Connection connection) throws SQLException { 
    CallableStatement call = connection.prepareCall("{ ? = call MYSCHEMA.MYFUNC(?,?) }"); 
    call.registerOutParameter(1, Types.INTEGER); // or whatever it is 
    call.setLong(2, id); 
    call.setLong(3, transId); 
    call.execute(); 
    int result = call.getInt(1); // propagate this back to enclosing class 
    } 
}); 
+0

ti dispiacerebbe fornirti un esempio? Sono un po 'perso come ottenere il risultato dalla funzione. Devo usare un parametro out? – Mauli

+0

Ho fornito un esempio sopra. A seconda della funzione/stored procedure potrebbe essere necessario utilizzare l'altro modulo di chiamata all'interno di 'prepareCall()' invece - la documentazione di CallableStatement descrive entrambi. – ChssPly76

+1

@ ChssPly76: Non ho mai saputo di 'doWork (..)' molto meglio di '@ NamedNativeQuery 'di Hibernate che richiede che sproc/function restituisca un refcursor come primo parametro (il parametro out). –

6

Sì, si ha bisogno di utilizzare un parametro out. Se si utilizza il metodo DoWork(), si farebbe qualcosa di simile:

session.doWork(new Work() { 
    public void execute(Connection conn) { 
     CallableStatement stmt = conn.prepareCall("? = call <some function name>(?)"); 
     stmt.registerOutParameter(1, OracleTypes.INTEGER); 
     stmt.setInt(2, <some value>); 
     stmt.execute(); 
     Integer outputValue = stmt.getInt(1); 
     // And then you'd do something with this outputValue 
    } 
}); 
+1

@ ChssPly76, l'ho scritto insieme a te, e dopo averlo pubblicato, il tuo era già lì. Né è una copia esatta, anche se ovviamente è abbastanza simile, poiché il problema è piuttosto semplice. Sarebbe bello se hai rimosso il tuo voto negativo. – Ladlestein

+1

L'etichetta comunemente accettata su SO è quella di cancellare la tua risposta se vedi che ce n'è un'altra proprio come già a meno che non siano stati scritti entrambi allo stesso tempo. In questo caso c'è una differenza di 16 minuti tra la mia e la tua. Se lo elimini ora, il rip perso a causa del down-vote verrà ripristinato nel tuo account durante il successivo ricalcolo della ripetizione (di solito vengono effettuati ogni 6-8 settimane). Il voto negativo non può essere annullato (è troppo vecchio) a meno che la tua risposta non venga modificata. – ChssPly76

+11

Oh. È anche un'etichetta comunemente accettata per votare in basso qualcuno in quei casi? – Ladlestein

1
public static void getThroHibConnTest() throws Exception { 
    logger.debug("UsersActiion.getThroHibConnTest() | BEG "); 
    Transaction tx = null; 
    Connection conn = null; 
    CallableStatement cs = null; 
    Session session = HibernateUtil.getInstance().getCurrentSession(); 
    try { 
     tx = session.beginTransaction(); 
     conn = session.connection(); 

     System.out.println("Connection = "+conn); 
     if (cs == null) 
     { 
      cs = 
       conn.prepareCall("{ ?=call P_TEST.FN_GETSUM(?,?) }"); 
     } 
     cs.clearParameters(); 
     cs.registerOutParameter(1,OracleTypes.INTEGER); 
     cs.setInt(2,1); 
     cs.setInt(3,2); 
     cs.execute(); 
     int retInt=cs.getInt(1); 
     tx.commit();    
    }catch (Exception ex) { 
     logger.error("UsersActiion.getThroHibConnTest() | ERROR | " , ex); 
     if (tx != null && tx.isActive()) { 
      try { 
       // Second try catch as the rollback could fail as well 
       tx.rollback(); 
      } catch (HibernateException e1) { 
       logger.debug("Error rolling back transaction"); 
      } 
      // throw again the first exception 
      throw ex; 
     } 
    }finally{ 
     try { 
      if (cs != null) { 
       cs.close(); 
       cs = null; 
      } 
      if(conn!=null)conn.close(); 

     } catch (Exception ex){;} 
    } 
    logger.debug("UsersActiion.getThroHibConnTest() | END "); 
} 
+2

il metodo 'connection()' è deprecato in 'Hibernate 3.3.2GA +' – Stephan

+1

Anche 'connection()' è deprecato. In quelle versioni questo è il modo in cui puoi accedere. – takacsot

4

codice alternativo :)

se si vuole risultato è possibile utilizzare il codice qui sotto dirigere

int result = session.doReturningWork(new ReturningWork<Integer>() { 
    @Override 
    public Integer execute(Connection connection) throws SQLException { 
    CallableStatement call = connection.prepareCall("{ ? = call MYSCHEMA.MYFUNC(?,?) }"); 
    call.registerOutParameter(1, Types.INTEGER); // or whatever it is 
    call.setLong(2, id); 
    call.setLong(3, transId); 
    call.execute(); 
    return call.getInt(1); // propagate this back to enclosing class 
    } 
}); 

http://keyurj.blogspot.com.tr/2012/12/dowork-in-hibernate.html

4

ho scritto an article about various ways of calling Oracle stored procedures and functions from Hibernate così, per riassumere, si hanno le seguenti opzioni:

  1. con un @NamedNativeQuery:

    @org.hibernate.annotations.NamedNativeQuery(
        name = "fn_my_func", 
        query = "{ ? = call MYSCHEMA.MYFUNC(?, ?) }", 
        callable = true, 
        resultClass = Integer.class 
    ) 
    
    Integer result = (Integer) entityManager.createNamedQuery("fn_my_func") 
        .setParameter(1, 1) 
        .setParameter(2, 1) 
        .getSingleResult();  
    
  2. con JDBC API:

    Session session = entityManager.unwrap(Session.class); 
    
    final AtomicReference<Integer> result = 
        new AtomicReference<>(); 
    
    session.doWork(connection -> { 
        try (CallableStatement function = connection 
          .prepareCall(
           "{ ? = call MYSCHEMA.MYFUNC(?, ?) }" 
          ) 
         ) { 
         function.registerOutParameter(1, Types.INTEGER); 
         function.setInt(2, 1); 
         function.setInt(3, 1); 
         function.execute(); 
         result.set(function.getInt(1)); 
        } 
    });    
    
  3. con un query Oracle nativa:

    Integer result = (Integer) entityManager.createNativeQuery(
        "SELECT MYSCHEMA.MYFUNC(:id, :transId) FROM DUAL") 
        .setParameter("postId", 1) 
        .setParameter("transId", 1) 
        .getSingleResult(); 
    
+0

Quando provo la prima opzione, risulta con la seguente eccezione. Solo FYI: org.hibernate.MappingException: Entità sconosciuta: java.lang.Integer – akaya

+0

Puoi biforcarti il ​​mio libro, Persistence Java ad alte prestazioni, GitHub Repository e [esegui il test tu stesso] (https://github.com/vladmihalcea/ ad alte prestazioni-java-persistenza/blob/8a2447eddda1b03371197db75c1ddecd32a8654e/core/src/test/java/com/vladmihalcea/libro/hpjp/ibernazione/sp/# OracleStoredProcedureTest.java L209). Ho appena eseguito con Hibernate 5.2.11 e funziona come un fascino. –

Problemi correlati