2009-11-09 4 views
14

Ma question est très semblable à Getting the return value of a PL/SQL function via HibernateComment appeler une fonction Oracle depuis Hibernate avec un paramètre de retour?

J'ai une fonction qui fait quelques modifications en interne et renvoie une valeur.

L'idée originale était de faire quelque chose comme ceci:

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(); 
} 

Malheureusement, cela ne fonctionne pas avec Oracle. Quelle est la manière recommandée de faire quelque chose comme ça?

Existe-t-il un moyen d'extraire des variables déclarées à partir de mon instruction?

Répondre

29

Hibernate Session fournit une méthode doWork() qui vous donne un accès direct à java.sql.Connection. Vous pouvez ensuite créer et utiliser java.sql.CallableStatement pour exécuter votre fonction:

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

pourriez-vous donner l'exemple? Je suis un peu perdu comment obtenir le résultat de la fonction. Ai-je besoin d'utiliser un paramètre out? – Mauli

+0

J'ai fourni un exemple ci-dessus. En fonction de votre fonction/procédure stockée, vous devrez peut-être utiliser l'autre formulaire d'appel dans prepareCall() '. La documentation CallableStatement décrit les deux. – ChssPly76

+1

@ ChssPly76: Je n'ai jamais connu 'doWork (..)' beaucoup mieux que '@ NamedNativeQuery' de Hibernate qui exige que le sproc/function retourne un refcursor comme premier paramètre (le paramètre out). –

6

Oui, vous avez besoin d'utiliser un paramètre out. Si vous utilisez la méthode DoWork(), vous feriez quelque chose comme ceci:

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, je l'ai écrit en même temps que vous, et après que je l'ai posté, le tien était déjà là. Il ne s'agit pas non plus d'une copie exacte, bien que ce soit assez similaire, puisque le problème est assez simple au départ. Ce serait bien si vous supprimiez votre vote négatif. – Ladlestein

+1

L'étiquette communément acceptée sur SO est de supprimer votre réponse si vous voyez qu'il y en a déjà une autre à moins qu'elles n'aient été littéralement écrites en même temps. Dans ce cas, il y a une différence de 16 minutes entre le mien et le vôtre. Si vous le supprimez maintenant, le rep perdu en raison d'un vote négatif sera restauré sur votre compte lors du prochain recalc de répétition (ils sont généralement effectués toutes les 6 à 8 semaines). Le vote négatif ne peut pas être annulé (c'est trop vieux) à moins que votre réponse ne soit éditée. – ChssPly76

+11

Oh. Est-ce que c'est aussi l'étiquette communément acceptée pour voter contre quelqu'un dans ces cas? – 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

la méthode 'connection()' est obsolète dans 'Hibernate 3.3.2GA +' – Stephan

+1

Même 'connection()' est obsolète. Dans ces versions, c'est ainsi que vous pouvez accéder. – takacsot

4

code alternatif :)

si vous voulez le résultat direct, vous pouvez utiliser le code ci-dessous

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

J'ai écrit an article about various ways of calling Oracle stored procedures and functions from Hibernate donc, pour résumer, vous avez les options suivantes:

  1. Avec 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. avec l'API JDBC:

    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. Avec un requête native Oracle:

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

Lorsque j'essaie la première option, il en résulte l'exception suivante. Juste FYI: org.hibernate.MappingException: Entité inconnue: java.lang.Integer – akaya

+0

Vous pouvez épingler mon livre, Persévérance Java haute performance, Référentiel GitHub et [exécuter le test vous-même] (https://github.com/vladmihalcea/ haute performance-java-persistance/blob/8a2447eddda1b03371197db75c1ddecd32a8654e/core/src/test/java/com/vladmihalcea/livre/hpjp/hibernate/sp/OracleStoredProcedureTest.java # L209). Je viens de le lancer avec Hibernate 5.2.11 et ça fonctionne comme un charme. –

Questions connexes