簡體   English   中英

我們如何使用 Hibernate 和 JPA 調用存儲過程?

[英]How can we call a stored procedure with Hibernate and JPA?

我們如何使用 Hibernate 或 JPA 調用存儲過程?

考慮以下僅返回基本返回值的存儲過程:

CREATE OR REPLACE PROCEDURE count_comments (  
   postId IN NUMBER,  
   commentCount OUT NUMBER )  
AS 
BEGIN 
    SELECT COUNT(*) INTO commentCount  
    FROM post_comment  
    WHERE post_id = postId; 
END;

您可以使用標准 JPA 調用此方法:

StoredProcedureQuery query = entityManager
    .createStoredProcedureQuery("count_comments")
    .registerStoredProcedureParameter(1, Long.class, 
        ParameterMode.IN)
    .registerStoredProcedureParameter(2, Long.class, 
        ParameterMode.OUT)
    .setParameter(1, 1L);

query.execute();

Long commentCount = (Long) query.getOutputParameterValue(2);

如果存儲過程返回 SYS_REFCURSOR:

CREATE OR REPLACE PROCEDURE post_comments ( 
   postId IN NUMBER, 
   postComments OUT SYS_REFCURSOR ) 
AS 
BEGIN
    OPEN postComments FOR
    SELECT *
    FROM post_comment 
    WHERE post_id = postId; 
END;

你可以這樣稱呼它:

StoredProcedureQuery query = entityManager
    .createStoredProcedureQuery("post_comments")
    .registerStoredProcedureParameter(1, Long.class, 
         ParameterMode.IN)
    .registerStoredProcedureParameter(2, Class.class, 
         ParameterMode.REF_CURSOR)
    .setParameter(1, 1L);

query.execute();

List<Object[]> postComments = query.getResultList();

如果要調用 Oracle 數據庫函數:

CREATE OR REPLACE FUNCTION fn_count_comments ( 
    postId IN NUMBER ) 
    RETURN NUMBER 
IS
    commentCount NUMBER; 
BEGIN
    SELECT COUNT(*) INTO commentCount 
    FROM post_comment 
    WHERE post_id = postId; 
    RETURN( commentCount ); 
END;

您不能使用StoredProcedureQuery因為它不適用於 Hibernate 5,因此您可以這樣調用它:

BigDecimal commentCount = (BigDecimal) entityManager
    .createNativeQuery(
        "SELECT fn_count_comments(:postId) FROM DUAL"
    )
    .setParameter("postId", 1L)
    .getSingleResult();

或使用普通的 JDBC:

Session session = entityManager.unwrap( Session.class ); 

Integer commentCount = session.doReturningWork( connection -> {
    try (CallableStatement function = connection.prepareCall(
            "{ ? = call fn_count_comments(?) }" )) {
        function.registerOutParameter( 1, Types.INTEGER );
        function.setInt( 2, 1 );
        function.execute();
        return function.getInt( 1 );
    }
} );

有關更多詳細信息,請查看以下文章:

您可以執行以下操作

 Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
PreparedStatement st = session.connection().prepareStatement("{call procedureName(?, ?)}");
                st.setString(1, formatter.format(parameter1));
                st.setString(2, formatter.format(parameter2));
                st.execute();
tx.commit();

請在需要的地方添加異常處理。

從休眠中調用存儲過程的一種方法

@NamedNativeQueries注釋中聲明您的存儲過程

//Stock.java

@NamedNativeQueries({
    @NamedNativeQuery(
    name = "callStockStoreProcedure",
    query = "CALL GetStocks(:stockCode)",
    resultClass = Stock.class
    )
})
@Entity
@Table(name = "stock")
public class Stock implements java.io.Serializable {

// Call it with getNamedQuery().

Query query = session.getNamedQuery("callStockStoreProcedure")
    .setParameter("stockCode", "7277");
List result = query.list();
for(int i=0; i<result.size(); i++){
    Stock stock = (Stock)result.get(i);
    System.out.println(stock.getStockCode());
}

這有效

要執行遠程過程,請使用以下結構:

映射

<sql-query name="RP">   
    {call some_rp(:param1, :param2)}
</sql-query>

Java代碼

session.getNamedQuery("RP").setInteger("param1", 1).setInteger("param2", 2).executeUpdate();

這是使用 Just IN 參數調用存儲過程的完整解決方案 ---

1) 創建存儲過程以作用於一個表或一組表:

CREATE OR REPLACE procedure insertHouseHello (
house_date in timestamp,
house_name in varchar2,
house_number in number,
house_value in float) 
is
begin
 insert into House("HOUSE_DATE","HOUSE_NAME","HOUSE_NUMBER","HOUSE_VALUE")
 values ( house_date, house_name,house_number,house_value);
 commit;
 end;

2) 從 SQL Prompt 執行存儲過程以檢查輸入。 當您從 Java/Hibernate 調用該過程時,您也應該看到類似的結果:

exec insertHouseHello(sysdate,'one',123,104); 

3)在Java代碼中:

log.info("Now trying to call the Stored Procedure*****************");
Query exQuery = session.createSQLQuery("CALL " +
        "insertHouseHello(:timestmp,:hname,:hno,:hvalue)");
exQuery.setParameter("timestmp", 
        new java.sql.Timestamp(Calendar.getInstance().getTime().getTime()));
exQuery.setParameter("hname", 34);
exQuery.setParameter("hno", 212);
exQuery.setParameter("hvalue", 12);
int exRows = exQuery.executeUpdate();
log.info("Executed Rows from Stored Procedure****************"+exRows);

4)現在檢查表中的結果,應該相應地更新:

Hibernate 通過存儲過程和函數提供對查詢的支持。 例如,如果我們有以下存儲過程,

CREATE OR REPLACE FUNCTION selectAllEmployments
RETURN SYS_REFCURSOR
AS
    st_cursor SYS_REFCURSOR;
BEGIN
    OPEN st_cursor FOR
 SELECT EMPLOYEE, EMPLOYER,
 STARTDATE, ENDDATE,
 REGIONCODE, EID, VALUE, CURRENCY
 FROM EMPLOYMENT;
      RETURN  st_cursor;
 END;

其中返回所有員工的列表。 存儲過程/函數必須返回一個結果集作為第一個輸出參數才能使用 Hibernate。

要在 Hibernate 中使用上述查詢,您需要通過命名查詢來映射它。

<sql-query name="selectAllEmployees_SP" callable="true">
    <return alias="emp" class="Employment">
        <return-property name="employee" column="EMPLOYEE"/>
        <return-property name="employer" column="EMPLOYER"/>
        <return-property name="startDate" column="STARTDATE"/>
        <return-property name="endDate" column="ENDDATE"/>
        <return-property name="regionCode" column="REGIONCODE"/>
        <return-property name="id" column="EID"/>
        <return-property name="salary">
            <return-column name="VALUE"/>
            <return-column name="CURRENCY"/>
        </return-property>
    </return>
    { ? = call selectAllEmployments() }
</sql-query>

使用存儲過程的規則/限制:

  • 無法使用 setFirstResult()/setMaxResults() 對存儲過程查詢進行分頁。
  • 推薦的調用形式是標准 SQL92: { ? = call functionName(<parameters>) } { ? = call functionName(<parameters>) }{ ? = call procedureName(<parameters>} { ? = call procedureName(<parameters>} 。不支持本機調用語法。

For Oracle the following rules apply:

  • 一個函數必須返回一個結果集。
  • 過程的第一個參數必須是返回結果集的 OUT。 這是通過在 Oracle 9 或 10 中使用 SYS_REFCURSOR 類型來完成的。在 Oracle 中,您需要定義一個 REF CURSOR 類型。 有關詳細信息,請參閱 Oracle 文獻。

For Sybase or MS SQL server the following rules apply:

  • 該過程必須返回一個結果集。 請注意,由於這些服務器可以返回多個結果集和更新計數,Hibernate 將迭代結果並將作為結果集的第一個結果作為其返回值。 其他一切都將被丟棄。
  • 如果您可以在您的過程中啟用 SET NOCOUNT ON,它可能會更有效率,但這不是必需的。

來源參考:來自官方的 Hibernate 文檔。

一種方法是使用 getNamedQuery()。

Query query = session.getNamedQuery("callStockStoreProcedure")
    .setParameter("stockCode", "7277");
List result = query.list();
for(int i=0; i<result.size(); i++){
    Stock stock = (Stock)result.get(i);
    System.out.println(stock.getStockCode());
}

您必須映射或使用注釋

還有其他: 來源

你應該去官方的hibernate文檔站點。 無論如何這里是直接帶你到存儲過程部分的鏈接

這種調用過程的簡單而聰明的方法:

 @Query(value = "call SCHEMA.PROCEDURE_NAME()" , nativeQuery = true)

如果您的過程包含參數,您可以像下面這樣調用它:

@Transactional
@Modifying
@Query(value = "call SCHEMA.PROCEDURE_NAME(:param1 , :param2)" , nativeQuery = true)
void validateUpdatePaymentStatus(@Param("param1") String param1, @Param("param2") int param2);

如果您的過程包含更新、插入或刪除使用

@Transactional
@Modifying

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM