SpringBoot 3+Hibernate 6升级后调用SQL Server sp_getapplock返回-999错误求助
SpringBoot 3+Hibernate 6升级后调用SQL Server sp_getapplock返回-999错误求助
我最近把一个已经跑在生产环境的SpringBoot 2.x.x老应用,升级到了最新的v3.4.2版本。这次升级逼得我修复了一堆依赖问题,其中最关键的两个是:
- 把Hibernate从v5.x.x版本升级到了v6.6.8-Final
- 完成了从javax到Jakarta EE的全量迁移
这个应用里用到了SQL Server的sp_getapplock系统存储过程来实现运行时的数据库锁,按照官方要求,这个存储过程默认必须在事务内部执行。为了统一处理返回的状态码,我还把它包在了一个自定义的存储过程里,代码定义如下:
CREATE PROCEDURE sp_getapplock_wrapper @Resource NVARCHAR(255), @LockMode NVARCHAR(32), @LockTimeout as INT, @Result INT OUTPUT AS BEGIN EXEC @Result = Sp_getapplock @Resource = @Resource, @LockMode = @LockMode, @LockTimeout = @LockTimeout; END;
但升级完成后,每次调用这个包装后的存储过程,都会返回-999的错误码,完全没法正常加锁。奇怪的是,如果我把@LockOwner从默认的'Transaction'改成'Session',一切就都正常了——这说明问题肯定出在事务层面。
我翻了应用日志,看起来事务是正常开启的,不管是Java端还是数据库端都有对应的日志输出:
[nio-8081-exec-1] o.s.orm.jpa.JpaTransactionManager : Creating new transaction with name [com.extoxesses.poc.core.service.CoreService.startProcess]: PROPAGATION_REQUIRED,ISOLATION_SERIALIZABLE [nio-8081-exec-1] .i.SessionFactoryImpl$SessionBuilderImpl : Opening Hibernate Session. tenant=null [nio-8081-exec-1] o.h.r.j.i.LogicalConnectionManagedImpl : `hibernate.connection.provider_disables_autocommit` was enabled. This setting should only be enabled when you are certain that the Connections given to Hibernate by the ConnectionProvider have auto-commit disabled. Enabling this setting when the Connections do not have auto-commit disabled will lead to Hibernate executing SQL operations outside of any JDBC/SQL transaction. [nio-8081-exec-1] org.hibernate.internal.SessionImpl : Opened Session [42330797-b6ac-4444-b8ed-edddb2d47029] at timestamp: 1740480200789 [nio-8081-exec-1] o.s.orm.jpa.JpaTransactionManager : Opened new EntityManager [SessionImpl(4729152PersistenceContext[entityKeys=[], collectionKeys=[]];ActionQueue[insertions=ExecutableList{size=0} updates=ExecutableList{size=0} deletions=ExecutableList{size=0} orphanRemovals=ExecutableList{size=0} collectionCreations=ExecutableList{size=0} collectionRemovals=ExecutableList{size=0} collectionUpdates=ExecutableList{size=0} collectionQueuedOps=ExecutableList{size=0} unresolvedInsertDependencies=null])] for JPA transaction [nio-8081-exec-1] o.h.e.t.internal.TransactionImpl : On TransactionImpl creation, JpaCompliance#isJpaTransactionComplianceEnabled == false [nio-8081-exec-1] o.h.e.t.internal.TransactionImpl : begin [nio-8081-exec-1] cResourceLocalTransactionCoordinatorImpl : ResourceLocalTransactionCoordinatorImpl#afterBeginCallback [nio-8081-exec-1] o.s.orm.jpa.JpaTransactionManager : Exposing JPA transaction as JDBC [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle@71b08367]
存储过程的调用日志看起来也没问题:
[nio-8081-exec-1] c.e.poc.core.service.LockService : Acquiring lock for id id [nio-8081-exec-1] o.h.p.internal.ProcedureCallImpl : Preparing procedure call : org.hibernate.sql.exec.internal.JdbcCallImpl@3b078a57 [nio-8081-exec-1] org.hibernate.SQL : {call sp_getapplock_wrapper(?, ?, ?, ?)} Hibernate: {call sp_getapplock_wrapper(?, ?, ?, ?)} [nio-8081-exec-1] o.h.r.j.i.ResourceRegistryStandardImpl : Registering statement [HikariProxyCallableStatement@1877695894 wrapping sp_executesql SQL: null] [nio-8081-exec-1] org.hibernate.orm.jdbc.bind : binding parameter (1:VARCHAR) <- [id] [nio-8081-exec-1] org.hibernate.orm.jdbc.bind : binding parameter (2:VARCHAR) <- [Exclusive] [nio-8081-exec-1] org.hibernate.orm.jdbc.bind : binding parameter (3:INTEGER) <- [0] [nio-8081-exec-1] o.hibernate.result.internal.OutputsImpl : Building Return [isResultSet=false, updateCount=-1, extendedReturn=false] [nio-8081-exec-1] org.hibernate.orm.jdbc.extract : extracted procedure output parameter ([4] : [INTEGER]) - [-999]
事务的回滚/提交流程也正常执行:
[nio-8081-exec-1] o.h.r.j.i.ResourceRegistryStandardImpl : Releasing statement [HikariProxyCallableStatement@1877695894 wrapping sp_executesql SQL: EXEC sp_getapplock_wrapper @P0 , @P1 , @P2 , @P3 OUT] [nio-8081-exec-1] o.h.r.j.i.ResourceRegistryStandardImpl : Closing prepared statement [HikariProxyCallableStatement@1877695894 wrapping sp_executesql SQL: EXEC sp_getapplock_wrapper @P0 , @P1 , @P2 , @P3 OUT] [nio-8081-exec-1] o.h.r.j.i.ResourceRegistryStandardImpl : Releasing statement [HikariProxyCallableStatement@1877695894 wrapping sp_executesql SQL: EXEC sp_getapplock_wrapper @P0 , @P1 , @P2 , @P3 OUT] [nio-8081-exec-1] o.h.r.j.i.ResourceRegistryStandardImpl : HHH000387: ResultSet's statement was not registered [nio-8081-exec-1] o.h.r.j.i.ResourceRegistryStandardImpl : Closing prepared statement [HikariProxyCallableStatement@1877695894 wrapping sp_executesql SQL: EXEC sp_getapplock_wrapper @P0 , @P1 , @P2 , @P3 OUT] [nio-8081-exec-1] o.h.r.j.i.ResourceRegistryStandardImpl : Exception clearing maxRows/queryTimeout [L'istruzione è chiusa.] [nio-8081-exec-1] c.e.poc.core.service.LockService : Cannot acquire lock for id id. Code from sql: -999 [nio-8081-exec-1] o.s.orm.jpa.JpaTransactionManager : Initiating transaction rollback [nio-8081-exec-1] o.s.orm.jpa.JpaTransactionManager : Rolling back JPA transaction on EntityManager [SessionImpl(4729152PersistenceContext[entityKeys=[], collectionKeys=[]];ActionQueue[insertions=ExecutableList{size=0} updates=ExecutableList{size=0} deletions=ExecutableList{size=0} orphanRemovals=ExecutableList{size=0} collectionCreations=ExecutableList{size=0} collectionRemovals=ExecutableList{size=0} collectionUpdates=ExecutableList{size=0} collectionQueuedOps=ExecutableList{size=0} unresolvedInsertDependencies=null])] [nio-8081-exec-1] o.h.e.t.internal.TransactionImpl : rolling back [nio-8081-exec-1] j.i.AbstractLogicalConnectionImplementor : Preparing to rollback transaction via JDBC Connection.rollback() [nio-8081-exec-1] j.i.AbstractLogicalConnectionImplementor : Transaction rolled-back via JDBC Connection.rollback() [nio-8081-exec-1] j.i.AbstractLogicalConnectionImplementor : LogicalConnection#afterTransaction [nio-8081-exec-1] o.h.r.j.i.ResourceRegistryStandardImpl : Releasing JDBC resources [nio-8081-exec-1] cResourceLocalTransactionCoordinatorImpl : ResourceLocalTransactionCoordinatorImpl#afterCompletionCallback(false) [nio-8081-exec-1] .t.i.SynchronizationRegistryStandardImpl : SynchronizationRegistryStandardImpl.notifySynchronizationsAfterTransactionCompletion(5) [nio-8081-exec-1] org.hibernate.internal.SessionImpl : SessionImpl#afterTransactionCompletion(successful=false, delayed=false) [nio-8081-exec-1] o.s.orm.jpa.JpaTransactionManager : Closing JPA EntityManager [SessionImpl(4729152PersistenceContext[entityKeys=[], collectionKeys=[]];ActionQueue[insertions=ExecutableList{size=0} updates=ExecutableList{size=0} deletions=ExecutableList{size=0} orphanRemovals=ExecutableList{size=0} collectionCreations=ExecutableList{size=0} collectionRemovals=ExecutableList{size=0} collectionUpdates=ExecutableList{size=0} collectionQueuedOps=ExecutableList{size=0} unresolvedInsertDependencies=null])] after transaction [nio-8081-exec-1] org.hibernate.internal.SessionImpl : Closing session [42330797-b6ac-4444-b8ed-edddb2d47029] [nio-8081-exec-1] o.h.e.jdbc.internal.JdbcCoordinatorImpl : Closing JDBC container [org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl@ea72b31] [nio-8081-exec-1] o.h.r.j.i.ResourceRegistryStandardImpl : Releasing JDBC resources [nio-8081-exec-1] o.h.r.j.i.LogicalConnectionManagedImpl : Closing logical connection [nio-8081-exec-1] o.h.r.j.i.ResourceRegistryStandardImpl : Releasing JDBC resources [nio-8081-exec-1] o.h.r.j.i.LogicalConnectionManagedImpl : Logical connection closed
我还发现一个奇怪的现象:如果在调用这个存储过程之前,先执行一个repository.save()操作,存储过程就能正常返回,锁也能加上。这感觉像是事务管理有某种“惰性”机制,直到有实际的数据库写操作才会真正激活事务?
我在网上搜了一圈,没找到能直接解决问题的资料,只看到Hibernate的一个PR提到过新版本里事务管理逻辑有变化。为了方便排查,我还专门写了一个极简的POC来复现这个问题。
有没有大佬遇到过类似的问题?求指点!
备注:内容来源于stack exchange,提问作者exto
相关产品推荐
相关产品推荐

