如何使用Spring Data JPA& amp;执行H2存储过程?休眠?

时间:2015-08-31 20:31:26

标签: hibernate jpa stored-procedures spring-data-jpa h2

我想使用Spring Data JPA和Hibernate执行一个简单的H2数据库存储过程。

存储过程类:

<

存储过程别名:

public class H2StoredProcedures {
    public static String stringIn(final String inValue) {
        log.info("stringIn: '{}'", inValue);
        return inValue + "_result";
    }
}

在H2控制台中执行按预期工作:

DROP ALIAS IF EXISTS STRING_IN;
CREATE ALIAS STRING_IN FOR "H2StoredProcedures.stringIn";

Spring Data JPA Repository Class:

CALL STRING_IN('fooIn');
PUBLIC.STRING_IN('fooIn')  
fooIn_result
(1 row, 1 ms)

存储库测试:

@Procedure("STRING_IN")
String stringIn(@Param("inValue") final String inValue);

结果如下:

@Test
public void testStringIn() throws Exception {
    assertEquals("fooIn_result", this.testRepository.stringIn("fooIn"));
}

使用以下存储过程对MS SQL Server数据库执行的相同代码按预期工作:

2015-08-31 14:52:38,117 WARN  [main]: HHH000456: Named parameters are used for a callable statement, but database metadata indicates named parameters are not supported. [logger=org.hibernate.procedure.internal.ProcedureCallImpl, mdc={}]
Hibernate: {call STRING_IN(?,?)}
2015-08-31 14:52:38,119 INFO  [main]: AtomikosNonXADataSourceBean 'adminDataSource1': getConnection ( null )... [logger=com.atomikos.jdbc.nonxa.AtomikosNonXADataSourceBean, mdc={}]
2015-08-31 14:52:38,128 WARN  [main]: Error delegating 'prepareCall' call [logger=com.atomikos.jdbc.JdbcConnectionProxyHelper, mdc={}]
org.h2.jdbc.JdbcSQLException: Method "STRING_IN (H2StoredProcedures, parameter count: 2)" not found; SQL statement:
 call STRING_IN(?,?)  [90087-187]
    at org.h2.message.DbException.getJdbcSQLException(DbException.java:345)
    at org.h2.message.DbException.get(DbException.java:179)
    at org.h2.message.DbException.get(DbException.java:155)
    at org.h2.engine.FunctionAlias.findJavaMethod(FunctionAlias.java:272)
    at org.h2.expression.JavaFunction.<init>(JavaFunction.java:32)
    at org.h2.command.Parser.readJavaFunction(Parser.java:2364)
    [...]
    at org.h2.server.TcpServerThread.run(TcpServerThread.java:159)
    at java.lang.Thread.run(Thread.java:745)

    at org.h2.engine.SessionRemote.done(SessionRemote.java:622)
    at org.h2.command.CommandRemote.prepare(CommandRemote.java:68)
    at org.h2.command.CommandRemote.<init>(CommandRemote.java:45)
    at org.h2.engine.SessionRemote.prepareCommand(SessionRemote.java:492)
    at org.h2.jdbc.JdbcConnection.prepareCommand(JdbcConnection.java:1189)
    at org.h2.jdbc.JdbcPreparedStatement.<init>(JdbcPreparedStatement.java:72)
    at org.h2.jdbc.JdbcCallableStatement.<init>(JdbcCallableStatement.java:52)
    at org.h2.jdbc.JdbcConnection.prepareCall(JdbcConnection.java:899)
    [...]
    at com.sun.proxy.$Proxy72.prepareCall(Unknown Source)
    at org.hibernate.engine.jdbc.internal.StatementPreparerImpl$1.doPrepare(StatementPreparerImpl.java:103)
    at org.hibernate.engine.jdbc.internal.StatementPreparerImpl$StatementPreparationTemplate.prepareStatement(StatementPreparerImpl.java:186)
    at org.hibernate.engine.jdbc.internal.StatementPreparerImpl.prepareStatement(StatementPreparerImpl.java:96)
    at org.hibernate.procedure.internal.ProcedureCallImpl.buildOutputs(ProcedureCallImpl.java:407)
    at org.hibernate.procedure.internal.ProcedureCallImpl.getOutputs(ProcedureCallImpl.java:378)
    at org.hibernate.jpa.internal.StoredProcedureQueryImpl.outputs(StoredProcedureQueryImpl.java:251)
    at org.hibernate.jpa.internal.StoredProcedureQueryImpl.execute(StoredProcedureQueryImpl.java:234)
    at org.springframework.data.jpa.repository.query.JpaQueryExecution$ProcedureExecution.doExecute(JpaQueryExecution.java:299)
    [...]
    at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodIntercceptor.invoke(CrudMethodMetadataPostProcessor.java:122)
    [...]
    at com.sun.proxy.$Proxy71.stringIn(Unknown Source)
    at TestRepositoryTest.testStringIn(TestRepositoryTest.java:227)
    [...]
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
2015-08-31 14:52:38,131 WARN  [main]: SQL Error: 90087, SQLState: 90087 [logger=org.hibernate.engine.jdbc.spi.SqlExceptionHelper, mdc={}]
2015-08-31 14:52:38,131 ERROR [main]: Method "STRING_IN (H2StoredProcedures, parameter count: 2)" not found; SQL statement:
 call STRING_IN(?,?)  [90087-187] [logger=org.hibernate.engine.jdbc.spi.SqlExceptionHelper, mdc={}]

似乎org.springframework.data.jpa.repository.query.StoredProcedureJpaQuery #creinStoredProcedure()或者更可能是Hibernates org.hibernate.jpa.internal.StoredProcedureQueryImpl #registerStoredProcedureParameter(String,Class, ParameterMode)应该知道方言。

我尝试使用@NamedStoredProcedureQuery,基本上每个注释组合都得到了基本相同的错误,没有找到包含2个参数的过程。

我还尝试将带有输入/输出参数的版本添加到H2StoredProcedures:

CREATE PROCEDURE STRING_IN
    @InValue SYSNAME,
    @OutValue NVARCHAR(255) OUTPUT
AS 
BEGIN
    SET NOCOUNT ON;
    SET @OutValue = @InValue + '_result';
    RETURN;
END;

如果我使用String返回类型设置进/出存储库方法:

public static void stringInOut(final String inValue, String outValue) {
    log.info("stringInOut: '{}'", inValue);
    outValue = inValue + "_result";
}

我找不到第三个参数:

@Procedure(procedureName = "STRING_IN_OUT")
String stringInOut(@Param("inValue") final String inValue, @Param("outValue") String outValue);

如果我使用void返回类型设置进/出存储库方法:

2015-08-31 15:19:15,517 ERROR [main]: Method "STRING_IN_OUT (H2StoredProcedures, parameter count: 3)" not found; SQL statement:
 call STRING_IN_OUT(?,?,?)  [90087-187] [logger=org.hibernate.engine.jdbc.spi.SqlExceptionHelper, mdc={}]

我得到一个错误,大概是尝试将结果映射到void:

@Procedure(procedureName = "STRING_IN_OUT")
void stringInOut(@Param("inValue") final String inValue, @Param("outValue") String outValue);

1 个答案:

答案 0 :(得分:3)

我做了一些调试,简短的回答是Hibernate JPA(至少5.0.0及以下版本)不支持H2Dialect的存储过程。

H2Dialect继承Dialect.getCallableStatementSupport(),它返回StandardCallableStatementSupport.NO_REF_CURSOR_INSTANCE。标准可调用语句支持不能正确处理H2“out”参数,该参数是Java返回值而不是语句参数。我尝试扩展H2Dialect和StandardCallableStatementSupport类来创建支持H2可调用语句的版本。然后我在org.hibernate.procedure.internal.ProcedureCallImpl#buildOutputs方法中遇到了问题。此方法实现了它自己的语句准备,而不是使用CallableStatementSupport #registerParameters方法。似乎没有一种干净的方式来扩展ProcedureCallImpl或buildOutputs,而且许多待办事项,包括“TOTAL PROOF-OF-CONCEPT !!!!!”,并没有给我很大的信心。我尝试了该类的修改版本,以查看是否在语句中不包括输出参数甚至可以工作。我发现虽然它调用了程序但它不知道如何处理结果。