如何使用Hibernate或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 );
}
} );
查看以下文章以获取更多详细信息:
EntityManager.createStoredProcedureQuery
方法,如果您的类路径中存在JPA 2.0,则不会解析它。https://stackoverflow.com/a/34174204/402488 - mjalil执行远程过程需要使用以下结构:
映射
<sql-query name="RP">
{call some_rp(:param1, :param2)}
</sql-query>
Java 代码
session.getNamedQuery("RP").setInteger("param1", 1).setInteger("param2", 2).executeUpdate();
从Hibernate调用存储过程的一种方法
在@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());
}
这个有效
你可以做以下事情
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();
一种方法是使用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());
}
你必须进行映射或使用注释
还有其他的:source
这是一种简单而智能的调用过程的方式:
@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
这里是使用仅 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提示符中执行存储过程以检查输入。当您从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) 现在检查表格中的结果,应该相应地得到更新:
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>
使用存储过程的规则/限制:
{ ? = call functionName(<parameters>) }
或{ ? = call procedureName(<parameters>}
。不支持本地调用语法。对于Oracle,应遵循以下规则:
对于Sybase或MS SQL Server,应遵循以下规则: