2009-11-09 14 views
14

Moje pytanie jest bardzo podobne do: Getting the return value of a PL/SQL function via HibernateJak wywołać funkcję Oracle z Hibernate z parametrem zwracającym?

Mam funkcję, która wykonuje pewne modyfikacje wewnętrznie i zwraca wartość.

Oryginalny pomysł żeby zrobić coś takiego:

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

Niestety to nie działa z Oracle. Jaki jest zalecany sposób zrobienia czegoś takiego?

Czy istnieje sposób wyodrębniania zadeklarowanych zmiennych z mojego wyciągu?

Odpowiedz

29

Sesja hibernacji zapewnia metodę doWork(), która umożliwia bezpośredni dostęp do java.sql.Connection. Następnie można utworzyć i używać java.sql.CallableStatement wykonywać swoją funkcję:

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

Czy mógłbyś podać przykład? Jestem trochę zagubiony, jak uzyskać wynik z funkcji. Czy muszę użyć parametru out? – Mauli

+0

Podałem przykład powyżej. W zależności od funkcji/procedury składowanej może zaistnieć potrzeba użycia innego formularza wywołania w ramach metody "prepareCall()" - dokumentacja CallableStatement opisuje oba. – ChssPly76

+1

@ ChssPly76: Nigdy nie wiedziałem o 'doWork (..)' o wiele lepszym niż '@NamedNativeQuery' Hibernate'a, który wymaga, aby funkcja/proces zwracała refkursor jako pierwszy parametr (parametr out). –

6

Tak, trzeba używać parametr wyjściowy. Jeśli używasz metody DoWork(), można zrobić coś takiego:

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, napisałem to w tym samym czasie co ty, a po tym, jak to opublikowałem, twoje już tam było. Nie jest to dokładna kopia, chociaż oczywiście jest dość podobna, ponieważ problem jest dość prosty. Byłoby miło, gdybyś usunął swój głos w dół. – Ladlestein

+1

Powszechnie akceptowana etykieta na SO polega na usunięciu odpowiedzi, jeśli zauważysz, że jest inna taka jak ta, chyba że obie zostały napisane dosłownie w tym samym czasie. W tym przypadku różnica między kopalnią a twoją wynosi 16 minut. Jeśli usuniesz go teraz, powtórnie stracony z powodu negatywnego wyniku głosowania zostanie przywrócony na twoje konto podczas następnego ponownego recalc (zazwyczaj robią to co 6 do 8 tygodni). Głosowanie w dół nie może zostać odwołane (jest za stare), chyba że twoja odpowiedź zostanie zmieniona. – ChssPly76

+11

Oh. Czy w tych przypadkach powszechnie akceptowana jest etykieta? – 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

Metoda 'connection()' jest przestarzała w 'Hibernate 3.3.2GA +' – Stephan

+1

Nawet 'connection()' jest przestarzałe. W tych wersjach jest to sposób, w jaki można uzyskać dostęp. – takacsot

4

kod alternatywna :)

jeśli chcesz bezpośredni rezultat można skorzystać z poniższego kodu

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

pisałem an article about various ways of calling Oracle stored procedures and functions from Hibernate tak, aby ją podsumować, masz następujące opcje:

  1. z @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. z 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. z macierzyste zapytanie Oracle:

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

Po wypróbowaniu pierwszej opcji powoduje to następujący wyjątek. Tylko FYI: org.hibernate.MappingException: Nieznany byt: java.lang.Integer – akaya

+0

Możesz rozwidlić moją książkę, High-Performance Java Persistence, GitHub Repository i [uruchom test sam] (https://github.com/vladmihalcea/ high-performance-java-persistence/blob/8a2447eddda1b03371197db75c1ddecd32a8654e/core/src/test/java/com/vladmihalcea/book/hpjp/hibernate/sp/OracleStoredProcedureTest.java # L209). Właśnie uruchomiłem go z Hibernate 5.2.11 i działa jak urok. –

Powiązane problemy