当前位置: 首页 > 知识库问答 >
问题:

如何使用 Spring Data JPA 执行 H2 存储过程

高和通
2023-03-14

我想使用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数据JPA存储库类:

@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 . spring framework . data . JPA . repository . query . storedprocedurejpaquery # createStoredProcedure()或更有可能是Hibernates org . hibernate . JPA . internal . storedprocedurequeryimpl # registerStoredProcedureParameter(String,Class,ParameterMode)中有一个错误,它们应该知道方言。

我尝试使用@NamedStoredProcedureQuery和基本上每种注释组合,但在没有找到具有2个参数的过程时,我得到了基本相同的错误。

我还尝试将带有in/out参数的版本添加到H2StoredProcess中:

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

如果我使用 String 返回类型设置了 in/out 存储库方法:

@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 返回类型的 in/out 存储库方法:

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

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

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

共有1个答案

何正德
2023-03-14

我做了一些调试,简短的回答是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 方法。似乎没有一种干净的方式来扩展ProgresserCallImpl或buildOutputs,许多待办事项,包括“完全概念证明!!!!!”,并没有给我太多的信心。我尝试了该类的修改版本,以查看在语句中不包含输出参数是否有效。我发现,虽然它调用了程序,但它不知道如何处理结果。

 类似资料:
  • 作为Hibernate中BLOB值的后期创建钩子的替代方法,我认为数据库内触发器是更好的解决方案,因此我尝试编写以下触发器: 将与触发器一起使用,例如: 然而,我无法计算出构建语句的语法。 [2022-03-09 16:14:51][42601]ERROR:语法错误在或接近lo_oid [2022-03-09 16:14:51]位置:279 真的不可能像在其他语句中那样使用变量吗?

  • 我一直试图使用Hibernate执行Oracle存储过程。这不是用于生产,而是用于我正在研究的Java源代码解析项目。简单地说,我不能从Oracle存储过程返回值。 我搜索并阅读了SO、Hibernate Community/Documentations(原生SQL章节)链接中的所有相关链接,并尝试了这些建议,但不知何故无法让它们发挥作用。以下是我的来源--我只包括相关的部分。

  • 创建存储过程demoSp: CREATE PROCEDURE demoSp(IN inputParam VARCHAR(255), \ INOUT inOutParam INT) BEGIN DECLARE z INT; SET z = inOutParam + 1; SET inOutParam = z; SELE

  • 我创建了一个过程,如: 并试图像这样执行它: 但它会抛出以下错误: 错误:函数insert_user_ax_register(未知,未知,未知,带时区的时间戳,未知,未知,带时区的时间戳,未知,未知,未知,bigint)不存在SQL状态:42883提示:没有函数匹配给定的名称和参数类型。可能需要添加显式类型转换。角色: 16 请帮我解决这个问题。我是新来的pgsql,无法从谷歌找到任何解决方案。我

  • 问题内容: 我无法使用会话工厂从Java Hibernate调用存储过程 我已经编写了一个包含5个参数的sql过程,并返回了一个在MS SQL Studio中运行良好的结果集 我正在设置上述查询的参数 问题答案: 我只是通过以下代码解决了它…只需在CSV中传递参数。感谢帮助人员。 完美的作品:)