[英]How do I execute an H2 stored procedure with Spring Data JPA & Hibernate?
我想使用Spring Data JPA和Hibernate執行一個簡單的H2數據庫存儲過程。
存儲過程類:
public class H2StoredProcedures {
public static String stringIn(final String inValue) {
log.info("stringIn: '{}'", inValue);
return inValue + "_result";
}
}
存儲過程別名:
DROP ALIAS IF EXISTS STRING_IN;
CREATE ALIAS STRING_IN FOR "H2StoredProcedures.stringIn";
在H2控制台中執行按預期工作:
CALL STRING_IN('fooIn');
PUBLIC.STRING_IN('fooIn')
fooIn_result
(1 row, 1 ms)
Spring Data JPA Repository類:
@Procedure("STRING_IN")
String stringIn(@Param("inValue") final String inValue);
存儲庫測試:
@Test
public void testStringIn() throws Exception {
assertEquals("fooIn_result", this.testRepository.stringIn("fooIn"));
}
結果如下:
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={}]
具有以下存儲過程的MS SQL Server數據庫的相同代碼按預期工作:
CREATE PROCEDURE STRING_IN
@InValue SYSNAME,
@OutValue NVARCHAR(255) OUTPUT
AS
BEGIN
SET NOCOUNT ON;
SET @OutValue = @InValue + '_result';
RETURN;
END;
似乎org.springframework.data.jpa.repository.query.StoredProcedureJpaQuery #creinStoredProcedure()中存在一個錯誤,或者更可能是Hibernates org.hibernate.jpa.internal.StoredProcedureQueryImpl #registerStoredProcedureParameter(String,Class,ParameterMode)應該知道方言。
我嘗試使用@NamedStoredProcedureQuery,基本上每個注釋組合,我得到的基本相同的錯誤是沒有找到帶有2個參數的過程。
我還嘗試將帶有in / out參數的版本添加到H2StoredProcedures:
public static void stringInOut(final String inValue, String outValue) {
log.info("stringInOut: '{}'", inValue);
outValue = inValue + "_result";
}
如果我使用String返回類型設置進/出存儲庫方法:
@Procedure(procedureName = "STRING_IN_OUT")
String stringInOut(@Param("inValue") final String inValue, @Param("outValue") String outValue);
我找不到第三個參數:
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);
我得到一個錯誤,大概是嘗試將結果映射到void:
Caused by: org.hibernate.MappingException: No Dialect mapping for JDBC type: 0
at org.hibernate.dialect.TypeNames.get(TypeNames.java:87)
at org.hibernate.dialect.TypeNames.get(TypeNames.java:118)
at org.hibernate.dialect.Dialect.getHibernateTypeName(Dialect.java:653)
at org.hibernate.loader.custom.JdbcResultMetadata.getHibernateType(JdbcResultMetadata.java:93)
at org.hibernate.loader.custom.ScalarResultColumnProcessor.performDiscovery(ScalarResultColumnProcessor.java:62)
at org.hibernate.loader.custom.CustomLoader.autoDiscoverTypes(CustomLoader.java:498)
at org.hibernate.result.internal.OutputsImpl$CustomLoaderExtension.processResultSet(OutputsImpl.java:297)
at org.hibernate.result.internal.OutputsImpl.extractResults(OutputsImpl.java:152)
at org.hibernate.result.internal.OutputsImpl.extractCurrentResults(OutputsImpl.java:143)
at org.hibernate.result.internal.OutputsImpl.access$100(OutputsImpl.java:52)
at org.hibernate.result.internal.OutputsImpl$CurrentReturnState.buildOutput(OutputsImpl.java:203)
at org.hibernate.result.internal.OutputsImpl$CurrentReturnState.getOutput(OutputsImpl.java:187)
at org.hibernate.result.internal.OutputsImpl.getCurrent(OutputsImpl.java:108)
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.query.JpaQueryExecution.execute(JpaQueryExecution.java:77)
at org.springframework.data.jpa.repository.query.AbstractJpaQuery.doExecute(AbstractJpaQuery.java:100)
at org.springframework.data.jpa.repository.query.AbstractJpaQuery.execute(AbstractJpaQuery.java:91)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:393)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:371)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:61)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:281)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136)
... 43 more
我做了一些調試,簡短的回答是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 !!!!!”,並沒有給我很大的信心。 我嘗試了該類的修改版本,以查看是否在語句中不包括輸出參數甚至可以工作。 我發現雖然它調用了程序但它不知道如何處理結果。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.