开发者社区> 问答> 正文

Caused by: java.sql.SQLException: connection holde

我的一段代码,在线程池中跑会出现Caused by: java.sql.SQLException: connection holder is null

补充一下:如果多线程每次都新new 一个thread来执行是不会有问题的

错误堆栈:

Cause: java.sql.SQLException: connection holder is null

; 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

展开
收起
山海行 2023-07-05 21:32:35 175 0
3 条回答
写回答
取消 提交回答
  • 北京阿里云ACE会长

    connection held"异常通常意味着数据库连接没有被正确释放或关闭,导致连接池中的连接被占用,无法再次使用。这可能会导致应用程序出现性能问题,或者最终导致连接池无法提供足够的连接。

    以下是一些可能导致"connection held"异常的原因和解决方法:

    未正确关闭连接
    如果在使用完数据库连接后没有正确地关闭连接,连接池中的连接可能会被占用,无法再次使用。在使用完连接后,应该调用连接的close方法,将连接返回到连接池中。

    连接池配置不当
    如果连接池的最大连接数设置得太小,或者最大空闲时间设置得太长,可能会导致连接池中的连接被占用,无法再次使用。在配置连接池时,需要根据应用程序的实际需求来设置连接池的参数,以确保连接池能够提供足够的连接。

    长时间的事务
    如果事务执行时间过长,可能会导致连接被占用,无法再次使用。在设计应用程序时,需要考虑事务的执行时间,并尽可能缩短事务的执行时间。

    数据库死锁
    如果数据库中存在死锁,可能会导致连接被占用,无法再次使用。在设计数据库时,需要考虑并发访问的问题,尽可能减少数据库死锁的发生。

    2023-07-30 09:36:15
    赞同 展开评论 打赏
  • 值得去的地方都没有捷径

    出现 "java.sql.SQLException: connection holder is null" 错误通常表示在事务处理过程中,连接没有正确的持有者。这可能是由于多线程环境下的并发问题导致的。

    在你的情况下,当你将代码放在线程池中执行时,会出现该错误,但如果每次都新创建一个线程则不会出错。这表明在多线程环境下,对于同一个连接的访问没有正确地进行同步处理。

    为了解决这个问题,你可以采取以下几个步骤:

    1. 确保你使用的数据库连接是线程安全的。有些数据库连接池实现是线程安全的,可以在多线程环境下正确地管理连接。如果你使用的连接池不是线程安全的,考虑更换为线程安全的连接池实现。

    2. 确保在多线程环境下,每个线程都有自己的连接。不要共享连接对象。在每个线程中获取一个新的数据库连接,并确保在使用完之后关闭连接。

    3. 在代码中正确处理数据库连接的生命周期。确保在事务结束后,释放数据库连接资源,包括提交或回滚事务,并关闭连接。

    4. 如果你在使用 Spring 框架进行事务管理,请确保在事务完成后进行适当的清理和关闭数据库连接。

    如果你仍然遇到问题,请提供更多关于你的代码和使用的数据库连接池的信息,以便更好地理解问题的来源并提供进一步的帮助。

    2023-07-11 17:21:32
    赞同 展开评论 打赏
  • 问题已经解决

    /** * 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

    2023-07-06 12:14:12
    赞同 展开评论 打赏
问答分类:
问答地址:
问答排行榜
最热
最新

相关电子书

更多
Spring Cloud Alibaba - 重新定义 Java Cloud-Native 立即下载
The Reactive Cloud Native Arch 立即下载
JAVA开发手册1.5.0 立即下载