2009-11-09 41 views
14

Câu hỏi của tôi rất giống như Getting the return value of a PL/SQL function via HibernateLàm thế nào để gọi một hàm Oracle từ Hibernate với tham số trả về?

Tôi có một chức năng thực hiện một số sửa đổi nội bộ và nó trả về một giá trị.

Ý tưởng ban đầu là để làm một cái gì đó như thế này:

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

Thật không may điều này không làm việc với Oracle. Cách được khuyến nghị để làm điều gì đó như thế này?

Có cách nào để trích xuất các biến được khai báo từ trong câu lệnh của tôi không?

Trả lời

29

Phiên Hibernate cung cấp phương thức doWork() cho phép bạn truy cập trực tiếp vào java.sql.Connection. Sau đó, bạn có thể tạo và sử dụng java.sql.CallableStatement để thực hiện chức năng của mình:

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

bạn có muốn cung cấp ví dụ không? Tôi là một chút mất làm thế nào để có được kết quả từ chức năng. Tôi có cần sử dụng tham số ngoài không? – Mauli

+0

Tôi đã cung cấp một ví dụ ở trên. Tùy thuộc vào hàm của bạn/thủ tục lưu sẵn, bạn có thể cần phải sử dụng biểu mẫu yêu cầu khác trong 'prepareCall()' thay vào đó - tài liệu CallableStatement mô tả cả hai. – ChssPly76

+1

@ ChssPly76: Tôi không bao giờ biết 'doWork (..)' tốt hơn nhiều so với '@ NamedNativeQuery' của Hibernate đòi hỏi hàm sproc/function để trả về một refcursor làm tham số đầu tiên (tham số out). –

6

Có, bạn cần sử dụng tham số ngoài. Nếu bạn sử dụng phương pháp DoWork(), bạn muốn làm điều gì đó như thế này:

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, tôi đã viết nó cùng lúc với bạn, và sau khi tôi đăng nó, bạn đã ở đó rồi. Nó cũng không phải là một bản sao chính xác, mặc dù tất nhiên nó khá giống nhau, vì vấn đề khá đơn giản để bắt đầu. Sẽ thật tuyệt nếu bạn bỏ phiếu bầu của mình xuống. – Ladlestein

+1

Các nghi thức phổ biến được chấp nhận trên SO là xóa câu trả lời của bạn nếu bạn thấy rằng có một số khác giống như nó đã được trừ khi cả hai đều được viết theo nghĩa đen cùng một lúc. Trong trường hợp này có sự khác biệt 16 phút giữa tôi và của bạn. Nếu bạn xóa nó ngay bây giờ, đại diện bị mất do bỏ phiếu xuống sẽ được khôi phục vào tài khoản của bạn trong lần lặp lại tiếp theo (chúng thường được thực hiện sau mỗi 6 đến 8 tuần). Bỏ phiếu xuống không thể hủy bỏ (nó quá cũ) trừ khi câu trả lời của bạn được chỉnh sửa. – ChssPly76

+11

Ồ. Nó cũng thường được chấp nhận nghi thức để xuống bỏ phiếu một ai đó trong những trường hợp? – 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

phương thức 'connection()' không được chấp nhận trong 'Hibernate 3.3.2GA +' – Stephan

+1

Thậm chí 'connection()' không được chấp nhận. Trong các phiên bản này, đây là cách bạn có thể truy cập. – takacsot

4

đang Alternative :)

nếu bạn muốn trực tiếp dẫn bạn có thể sử dụng mã dưới đây

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

tôi đã viết an article about various ways of calling Oracle stored procedures and functions from Hibernate như vậy, để tóm tắt nó, bạn có các tùy chọn sau:

  1. Với @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. Với 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. Với một truy vấn Oracle gốc:

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

Khi tôi thử tùy chọn đầu tiên, kết quả có ngoại lệ sau. Chỉ cần FYI: org.hibernate.MappingException: Thực thể không xác định: java.lang.Integer – akaya

+0

Bạn có thể chia sách của mình, Hiệu suất cao Java Persistence, GitHub Repository và [chạy thử nghiệm cho mình] (https://github.com/vladmihalcea/ hiệu năng cao-java-persistence/blob/8a2447eddda1b03371197db75c1ddecd32a8654e/core/src/test/java/com/vladmihalcea/book/hpjp/hibernate/sp/OracleStoredProcedureTest.java # L209). Tôi vừa chạy nó với Hibernate 5.2.11 và nó hoạt động như một sự quyến rũ. –

Các vấn đề liên quan