可调用语句支持

发布者:    |       Hibernate ORM JPA

即将发布的JPA 2.1规范增加了处理JDBC可调用语句(存储过程和函数调用)的标准支持。Arun Gupta在https://blogs.oracle.com/arungupta/entry/jpa_2_1_early_draft中对JPA 2.1的初始功能进行了合理的总结,包括对存储过程的支持。在这里,“标准化”意味着跨提供者和数据库供应商。非常不错。尽管我很喜欢处理可调用语句的标准支持的想法,但我对提议的JPA StoredProcedureQuery API的一些方面并不十分兴奋。我的担忧主要集中在如何访问输出,尤其是当预期多个结果时。让我们首先看看一个返回结果的简单示例

StoredProcedreQuery query = entityManager.createStoredProcedureQuery( "top10SalesmenByQuarter", Employee.class );
query.registerStoredProcedureParameter( "quarter", String.class, ParameterMode.IN );
query.setParameter( "quarter", "Q1-2000" );
List top10Salesmen = query.getResultList();
...

这里没有什么令人讨厌的。

然而,想象一下,我们想调用一个既有更新计数又有结果的存储过程。在我看来,这就是StoredProcedureQuery变得有点可疑的地方。它很大程度上试图遵循JDBC访问混合返回的范例。当然,这种方法的优势是它对熟悉JDBC API的开发者来说很熟悉。让我们看看一个例子

StoredProcedreQuery query = entityManager.createStoredProcedureQuery( "mixedReturns" );
...
while( 1==1 ) {
    boolean isResult = query.hasMoreResults();
    if ( isResult ) {
        handleResult( query.getResultList() );
    }
    else {
        int updateCount = query.getUpdateCount();

        // complete exit condition is ( ! query.hasMoreResults() && query.getUpdateCount != -1 )
        if ( updateCount == -1 ) {
            break;
        }

        handleUpdateCount( updateCount );
    }
}
...

对我来说,这并不是非常用户友好。然而,我无法将该API的提议更改纳入。因此,我决定开发一个替代API;通过Session访问的Hibernate原生API。上述查询使用该API将看起来像

org.hibernate.procedure.ProcedureCall call = entityManager.unwrap( Session.class ).createStoredProcedureCall( "mixedReturns" );
...
org.hibernate.procedure.ProcedureResult callResult = call.getResult();

while ( callResult.hasMoreReturns() ) {
    final org.hibernate.result.Return return = callResult.getNextReturn();
    if ( org.hibernate.result.ResultSetReturn.class.isInstance( return ) ) {
        handleResult( ( (org.hibernate.result.ResultSetReturn) return ).getResultList() );
    }
    else {
        handleUpdateCount( (org.hibernate.result.UpdateCountReturn) return ).getUpdateCount() );
    }
}
...

这两个API都支持处理多个ResultSet。如果提供了返回类或结果集映射,则它们适用于所有处理过的ResultSet

StoredProcedreQuery query = entityManager.createStoredProcedureQuery( "top_and_bottom_salesmen_by_quarter", Employee.class );
query.registerStoredProcedureParameter( "quarter", String.class, ParameterMode.IN );
query.registerStoredProcedureParameter( "top_salesmen", String.class, ParameterMode.REF_CURSOR );
query.registerStoredProcedureParameter( "bottom_salesmen", String.class, ParameterMode.REF_CURSOR );
query.setParameter( "quarter", "Q1-2000" );

// we will end up with 2 result lists, where each list contains elements of type Employee.  Pretty sweet!

boolean isResult = query.hasMoreResults();
while( isResult ) {
    handleResult( query.getResultList() );
}
...
ProcedureCall call = entityManager.unwrap( Session.class ).createStoredProcedureCall( "top_and_bottom_salesmen_by_quarter", Employee.class );
call.registerParameter( "quarter", String.class, ParameterMode.IN );
call.registerParameter( "top_salesmen", String.class, ParameterMode.REF_CURSOR );
call.registerParameter( "bottom_salesmen", String.class, ParameterMode.REF_CURSOR );
call.setParameter( "quarter", "Q1-2000" );

// we will end up with 2 result lists, where each list contains elements of type Employee.  Pretty sweet!

ProcedureResult callResult = call.getResult();
while ( callResult.hasMoreReturns() ) {
    final ResultSetReturn rtn = (ResultSetReturn) callResult.getNextReturn();
    handleResult( rtn.getResultList() );
}
...

返回顶部