我的一段代码,在线程池中跑会出现Caused by: java.sql.SQLException: connection holder is null
补充一下:如果多线程每次都新new 一个thread来执行是不会有问题的
错误堆栈:
; uncategorized SQLException for SQL []; SQL state [null]; error code [0]; connection holder is null; nested exception is java.sql.SQLException: connection holder is null at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:83) ~[spring-jdbc-3.2.13.RELEASE.jar:3.2.13.RELEASE] at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:80) ~[spring-jdbc-3.2.13.RELEASE.jar:3.2.13.RELEASE] at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:80) ~[spring-jdbc-3.2.13.RELEASE.jar:3.2.13.RELEASE] at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:73) ~[mybatis-spring-1.2.2.jar:1.2.2] at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:371) ~[mybatis-spring-1.2.2.jar:1.2.2] at com.sun.proxy.$Proxy16.update(Unknown Source) ~[na:na] at org.mybatis.spring.SqlSessionTemplate.update(SqlSessionTemplate.java:254) ~[mybatis-spring-1.2.2.jar:1.2.2] at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:54) ~[mybatis-3.2.8.jar:3.2.8] at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:52) ~[mybatis-3.2.8.jar:3.2.8] at com.sun.proxy.$Proxy17.update(Unknown Source) ~[na:na] at com.youzan.pay.assetcenter.service.nsq.payNotify.TestTransactionSynchronization.afterCompletion(TestTransactionSynchronization.java:53) ~[classes/:na] at org.springframework.transaction.support.TransactionSynchronizationUtils.invokeAfterCompletion(TransactionSynchronizationUtils.java:168) ~[spring-tx-3.2.13.RELEASE.jar:3.2.13.RELEASE] at org.springframework.transaction.support.AbstractPlatformTransactionManager.invokeAfterCompletion(AbstractPlatformTransactionManager.java:994) [spring-tx-3.2.13.RELEASE.jar:3.2.13.RELEASE] at org.springframework.transaction.support.AbstractPlatformTransactionManager.triggerAfterCompletion(AbstractPlatformTransactionManager.java:969) [spring-tx-3.2.13.RELEASE.jar:3.2.13.RELEASE] at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:800) [spring-tx-3.2.13.RELEASE.jar:3.2.13.RELEASE] at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:724) [spring-tx-3.2.13.RELEASE.jar:3.2.13.RELEASE] at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:148) [spring-tx-3.2.13.RELEASE.jar:3.2.13.RELEASE] at com.youzan.pay.assetcenter.test.Nsq.TestNsqPayNotifyConsumer.testNotify(TestNsqPayNotifyConsumer.java:57) [test-classes/:na] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_91] at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_91] at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_91] at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_91] at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) [junit-4.12.jar:4.12] at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) [junit-4.12.jar:4.12] at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) [junit-4.12.jar:4.12] at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) [junit-4.12.jar:4.12] at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26) [junit-4.12.jar:4.12] at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74) [spring-test-3.2.13.RELEASE.jar:3.2.13.RELEASE] at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:83) [spring-test-3.2.13.RELEASE.jar:3.2.13.RELEASE] at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72) [spring-test-3.2.13.RELEASE.jar:3.2.13.RELEASE] at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:231) [spring-test-3.2.13.RELEASE.jar:3.2.13.RELEASE] at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:88) [spring-test-3.2.13.RELEASE.jar:3.2.13.RELEASE] at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) [junit-4.12.jar:4.12] at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) [junit-4.12.jar:4.12] at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) [junit-4.12.jar:4.12] at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) [junit-4.12.jar:4.12] at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) [junit-4.12.jar:4.12] at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61) [spring-test-3.2.13.RELEASE.jar:3.2.13.RELEASE] at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71) [spring-test-3.2.13.RELEASE.jar:3.2.13.RELEASE] at org.junit.runners.ParentRunner.run(ParentRunner.java:363) [junit-4.12.jar:4.12] at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:174) [spring-test-3.2.13.RELEASE.jar:3.2.13.RELEASE] at org.junit.runner.JUnitCore.run(JUnitCore.java:137) [junit-4.12.jar:4.12] at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69) [junit-rt.jar:na] at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:234) [junit-rt.jar:na] at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:74) [junit-rt.jar:na] Caused by: java.sql.SQLException: connection holder is null at com.alibaba.druid.pool.DruidPooledConnection.checkStateInternal(DruidPooledConnection.java:1122) ~[druid-1.0.24.jar:1.0.24] at com.alibaba.druid.pool.DruidPooledConnection.checkState(DruidPooledConnection.java:1113) ~[druid-1.0.24.jar:1.0.24] at com.alibaba.druid.pool.DruidPooledConnection.getAutoCommit(DruidPooledConnection.java:718) ~[druid-1.0.24.jar:1.0.24] at org.mybatis.spring.transaction.SpringManagedTransaction.openConnection(SpringManagedTransaction.java:82) ~[mybatis-spring-1.2.2.jar:1.2.2] at org.mybatis.spring.transaction.SpringManagedTransaction.getConnection(SpringManagedTransaction.java:67) ~[mybatis-spring-1.2.2.jar:1.2.2] at org.apache.ibatis.executor.BaseExecutor.getConnection(BaseExecutor.java:279) ~[mybatis-3.2.8.jar:3.2.8] at org.apache.ibatis.executor.SimpleExecutor.prepareStatement(SimpleExecutor.java:72) ~[mybatis-3.2.8.jar:3.2.8] at org.apache.ibatis.executor.SimpleExecutor.doUpdate(SimpleExecutor.java:47) ~[mybatis-3.2.8.jar:3.2.8] at org.apache.ibatis.executor.BaseExecutor.update(BaseExecutor.java:105) ~[mybatis-3.2.8.jar:3.2.8] at org.apache.ibatis.executor.CachingExecutor.update(CachingExecutor.java:71) ~[mybatis-3.2.8.jar:3.2.8] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_91] at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_91] at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_91] at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_91] at org.apache.ibatis.plugin.Invocation.proceed(Invocation.java:49) ~[mybatis-3.2.8.jar:3.2.8] at com.youzan.pay.assetcenter.dal.monitor.SqlMonitorManager.intercept(SqlMonitorManager.java:53) ~[classes/:na] at org.apache.ibatis.plugin.Plugin.invoke(Plugin.java:60) ~[mybatis-3.2.8.jar:3.2.8] at com.sun.proxy.$Proxy59.update(Unknown Source) ~[na:na] at org.apache.ibatis.session.defaults.DefaultSqlSession.update(DefaultSqlSession.java:152) ~[mybatis-3.2.8.jar:3.2.8] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_91] at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_91] at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_91] at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_91] at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:358) ~[mybatis-spring-1.2.2.jar:1.2.2] ... 40 common frames omitted
代码实现如下图:
【提问75】
测试代码:
public class TestNsqPayNotifyConsumer extends TestBase {
@Resource
private TestTransactionSynchronization testTransactionSynchronization;
@Test
public void testNotify() {
ExecutorService threadPoolExecutor = Executors.newSingleThreadExecutor();
while (true) {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
threadPoolExecutor.execute(new Runnable() {
@Override
public void run() {
transactionTemplate.execute(new TransactionCallback<Boolean>() {
@Override
public Boolean doInTransaction(TransactionStatus status) {
PayAcquireIdempotencyDO payAcquireIdempotencyDO = new PayAcquireIdempotencyDO();
payAcquireIdempotencyDO.setGmtCreate(new Date());
payAcquireIdempotencyDO.setIdempotencycolKey(UUID.randomUUID().toString());
payAcquireIdempotencyDO.setId(1);
payAcquireIdempotencyDAO.update(payAcquireIdempotencyDO);
TransactionSynchronizationManager.registerSynchronization(testTransactionSynchronization);
return Boolean.TRUE;
}
});
}
});
}
}
}
public class TestTransactionSynchronization extends TransactionSynchronizationAdapter {
/** 事务模板 PROPAGATION_REQUIRES_NEW */
@Resource
private TransactionTemplate requiresNewTransactionTemplate;
@Resource
protected PayAcquireIdempotencyDAO payAcquireIdempotencyDAO;
@Override
public void afterCompletion(int status) {
if (status == STATUS_COMMITTED) {
requiresNewTransactionTemplate.execute(new TransactionCallback<Boolean>() {
@Override
public Boolean doInTransaction(TransactionStatus status) {
PayAcquireIdempotencyDO payAcquireIdempotencyDO = new PayAcquireIdempotencyDO();
payAcquireIdempotencyDO.setGmtCreate(new Date());
payAcquireIdempotencyDO.setIdempotencycolKey(UUID.randomUUID().toString());
payAcquireIdempotencyDO.setId(1);
payAcquireIdempotencyDAO.update(payAcquireIdempotencyDO);
return Boolean.TRUE;
}
});
}
//这段代码移动到requiresNewTransactionTemplate模板中执行就是对的 PayAcquireIdempotencyDO payAcquireIdempotencyDO = new PayAcquireIdempotencyDO();
payAcquireIdempotencyDO.setGmtCreate(new Date());
payAcquireIdempotencyDO.setIdempotencycolKey(UUID.randomUUID().toString());
payAcquireIdempotencyDO.setId(1);
payAcquireIdempotencyDAO.update(payAcquireIdempotencyDO);
}
}
原提问者GitHub用户sinory
connection held"异常通常意味着数据库连接没有被正确释放或关闭,导致连接池中的连接被占用,无法再次使用。这可能会导致应用程序出现性能问题,或者最终导致连接池无法提供足够的连接。
以下是一些可能导致"connection held"异常的原因和解决方法:
未正确关闭连接
如果在使用完数据库连接后没有正确地关闭连接,连接池中的连接可能会被占用,无法再次使用。在使用完连接后,应该调用连接的close方法,将连接返回到连接池中。
连接池配置不当
如果连接池的最大连接数设置得太小,或者最大空闲时间设置得太长,可能会导致连接池中的连接被占用,无法再次使用。在配置连接池时,需要根据应用程序的实际需求来设置连接池的参数,以确保连接池能够提供足够的连接。
长时间的事务
如果事务执行时间过长,可能会导致连接被占用,无法再次使用。在设计应用程序时,需要考虑事务的执行时间,并尽可能缩短事务的执行时间。
数据库死锁
如果数据库中存在死锁,可能会导致连接被占用,无法再次使用。在设计数据库时,需要考虑并发访问的问题,尽可能减少数据库死锁的发生。
出现 "java.sql.SQLException: connection holder is null" 错误通常表示在事务处理过程中,连接没有正确的持有者。这可能是由于多线程环境下的并发问题导致的。
在你的情况下,当你将代码放在线程池中执行时,会出现该错误,但如果每次都新创建一个线程则不会出错。这表明在多线程环境下,对于同一个连接的访问没有正确地进行同步处理。
为了解决这个问题,你可以采取以下几个步骤:
确保你使用的数据库连接是线程安全的。有些数据库连接池实现是线程安全的,可以在多线程环境下正确地管理连接。如果你使用的连接池不是线程安全的,考虑更换为线程安全的连接池实现。
确保在多线程环境下,每个线程都有自己的连接。不要共享连接对象。在每个线程中获取一个新的数据库连接,并确保在使用完之后关闭连接。
在代码中正确处理数据库连接的生命周期。确保在事务结束后,释放数据库连接资源,包括提交或回滚事务,并关闭连接。
如果你在使用 Spring 框架进行事务管理,请确保在事务完成后进行适当的清理和关闭数据库连接。
如果你仍然遇到问题,请提供更多关于你的代码和使用的数据库连接池的信息,以便更好地理解问题的来源并提供进一步的帮助。
问题已经解决
/** * Invoked after transaction commit. Can perform further operations right * after the main transaction has successfully committed. *
Can e.g. commit further operations that are supposed to follow on a successful * commit of the main transaction, like confirmation messages or emails. *
NOTE: The transaction will have been committed already, but the * transactional resources might still be active and accessible. As a consequence, * any data access code triggered at this point will still "participate" in the * original transaction, allowing to perform some cleanup (with no commit following * anymore!), unless it explicitly declares that it needs to run in a separate * transaction. Hence: Use {@code PROPAGATION_REQUIRES_NEW} for any * transactional operation that is called from here. * @throws RuntimeException in case of errors; will be propagated to the caller * (note: do not throw TransactionException subclasses here!) */ void afterCommit();
如果不使用PROPAGATION_REQUIRES_NEW会是之前的连接不能释放,只是被关闭,导致问题
原回答者GitHub用户sinory
版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。