2009-11-09 21 views
14

Mi pregunta es muy similar a Getting the return value of a PL/SQL function via Hibernate¿Cómo llamar a una función de Oracle desde Hibernate con un parámetro de retorno?

Tengo una función que hace algunas modificaciones internamente y devuelve un valor.

La idea original era hacer algo como esto:

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

Desafortunadamente esto no funciona con Oracle. ¿Cuál es la forma recomendada de hacer algo como esto?

¿Hay una manera de extraer las variables declaradas dentro de mi estado de cuenta?

Respuesta

29

Session de Hibernate proporciona un método doWork() que le da acceso directo a java.sql.Connection. A continuación, puede crear y utilizar java.sql.CallableStatement para ejecutar su función:

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

¿le importaría dar un ejemplo? Estoy un poco perdido cómo obtener el resultado de la función. ¿Debo usar un parámetro out? – Mauli

+0

He proporcionado un ejemplo anterior. Dependiendo de su función/procedimiento almacenado, puede necesitar usar el otro formulario de invocación dentro de 'prepareCall()' en su lugar - la documentación de CallableStatement describe ambos. – ChssPly76

+1

@ ChssPly76: nunca supe de 'DoWork (..)' mucho mejor que Hibernate de '@ NamedNativeQuery' que requiere la sproc/función para devolver un refcursor como el primer parámetro (el parámetro de salida). –

6

Sí, usted tiene que utilizar un parámetro de salida. Si se utiliza el método DoWork(), que haría algo como esto:

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, lo escribí al mismo tiempo que tú, y después de publicarlo, el tuyo ya estaba allí. Tampoco es una copia exacta, aunque por supuesto es bastante similar, ya que el problema es bastante simple para empezar. Sería bueno si eliminas tu voto a la baja. – Ladlestein

+1

La etiqueta comúnmente aceptada en SO es eliminar tu respuesta si ves que hay otra igual que ya, a menos que ambas hayan sido escritas literalmente al mismo tiempo. En este caso, hay una diferencia de 16 minutos entre la mía y la tuya. Si lo elimina ahora, la reputación perdida debido a un voto negativo se restablecerá en su cuenta durante el siguiente rep recalc (generalmente se realizan cada 6 a 8 semanas). El voto a la baja no se puede anular (es demasiado viejo) a menos que se edite su respuesta. – ChssPly76

+11

Oh. ¿También es una etiqueta comúnmente aceptada votar por alguien en esos casos? – 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

el método 'connection()' está en desuso en 'Hibernate 3.3.2GA +' – Stephan

+1

Incluso 'connection()' está en desuso. En esas versiones, esta es la forma en que puede acceder. – takacsot

4

Alternativa Código :)

si quieres resultado que se puede utilizar a continuación el código dirigir

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

me escribió an article about various ways of calling Oracle stored procedures and functions from Hibernate tanto, para resumir, usted tiene las siguientes opciones:

  1. Con una @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 la 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. Con una consulta nativa de Oracle:

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

Cuando intento la primera opción, resulta con la siguiente excepción. Solo FYI: org.hibernate.MappingException: Entidad desconocida: java.lang.Integer – akaya

+0

Puede copiar mi libro, Persistencia de Java de alto rendimiento, Repositorio de GitHub y [ejecute la prueba usted mismo] (https://github.com/vladmihalcea/ alto rendimiento -java-persistencia/blob/8a2447eddda1b03371197db75c1ddecd32a8654e/core/src/test/java/com/vladmihalcea/book/hpjp/hibernate/sp/OracleStoredProcedureTest.java # L209). Lo acabo de ejecutar con Hibernate 5.2.11 y funciona como un encanto. –

Cuestiones relacionadas