使用jakarta.persistence.Query执行SQL函数时提交异常求助
问题根源
核心矛盾是Spring事务管理和PostgreSQL DO块内部的自主事务控制冲突:
- 方法上加了
@Transactional,Spring会自动开启一个外层事务 - 但你的PL/pgSQL DO块里包含多个
COMMIT/ROLLBACK语句,这些语句会直接操作数据库连接的事务状态,和Spring的外层事务逻辑完全冲突
解决方案
1. 移除方法上的@Transactional注解
你的DO块本身已经完成了事务的提交/回滚控制,不需要Spring再套一层事务。Spring的事务管理器会在方法结束时尝试提交已经被DO块提交过的连接,这就是"Unable to commit against JDBC Connection"报错的直接原因。
2. 恢复默认数据源配置
删掉以下两个配置,恢复默认值即可:
spring.datasource.hikari.auto-commit=false spring.jpa.properties.hibernate.connection.provider_disables_autocommit=true
没有@Transactional时,JDBC连接会使用自动提交模式,DO块内部的事务控制可以正常执行。
3. 修正Java代码
DO块是无返回值的匿名代码块,不需要指定Long.class参数,直接执行即可:
public int executeRunFunctions() { Query query = entityManager.createNativeQuery(loadQuery("/script4.sql")); query.executeUpdate(); // DO块执行后返回0,因为没有影响行数 return 0; }
报错原因详解
- 初始报错
invalid transaction termination:Spring开启的外层事务还未结束,DO块里的COMMIT试图终止这个事务,PostgreSQL不允许在一个已有事务的上下文中显式执行自主事务控制语句。 - 添加配置后的报错:虽然禁用了Hibernate的自动提交,但Spring事务管理器仍会在方法结束时尝试提交连接,而DO块已经提前完成了提交,连接处于已提交状态,再次提交就会报错,但DO块内的逻辑已经执行完毕,所以数据库中能看到执行结果。
额外注意
如果你的业务需要DO块和其他数据库操作在同一个事务中,那必须重构DO块,移除内部的COMMIT/ROLLBACK语句,把事务控制权交给Spring。但从你的DO块结构来看,它是一个包含多次提交的独立批量处理逻辑,这种场景下让它脱离Spring事务管理是更合理的选择。
内容的提问来源于stack exchange,提问作者Денис Кондратов
相关产品推荐
相关产品推荐

