项目中使用了/数据库连接池,但是发现运行着一段时间后,总会报
java.sql.SQLException: connection holder is null
查阅很多解决办法都说设置。
这两个参数就可以了,但我的项目里,并不是这种超时间使用连接没有回收而导致的,所以这种解决方法并不能解决我的问题。
而且druid版本已经是1.1.2最新的版本,排除了druid非bug原因,那就是我本身项目代码问题了。
在druid官方github问题里也有很多同学的发现问题发析原因,顿时有了思路。
参考连接: 、。
两个问题都指出跨线程使用druid而引发的,温少也建议不要跨线程使用数据连接。
所以经过两天的查资料,和不断地尝试,终于找出问题所在。
首先我的项目里是用了spring-boot,连接池配置这些就不贴出来了,都大同小异。
而我的事务管理也是用的是spring的 @Transactional 注入管理。
问题在于,我的一个service方法里的一个方法里,该方法在事务提交之后,会再次触发另一个service执行代码。
@Service@Transactional(value = "transaction", rollbackFor = Exception.class)public class a_service { @Autowired private Dao dao; @Autowired private ApplicationEventPublisher publisher; /** *该方法执行所有代码之后,会触发publisher.publishEvent方法 */ public String test(){ .....省略执行代码..... publisher.publishEvent(params); }}@Service@Transactional(value = "transaction", rollbackFor = Exception.class)public class b_service { @Autowired private Dao dao; /** * 当事务提交后, 才会真正的执行@TransactionalEventListener配置的Listener, 如果Listener抛异常, 方法返回失 * 败, 但事务不会回滚. */ @TransactionalEventListener(phase = TransactionPhase.AFTER_COMPLETION) public String test(){ dao.save(params); }}
问题就出于 b_service执行这里,当触发b_service方法时,b_service的事务隔离级别是默认PROPAGATION_REQUIRED,即如果当前存在事务则加入该事务
PROPAGATION_REQUIRED
加入当前正要执行的事务不在另外一个事务里,那么就起一个新的事务 比如说,ServiceB.methodB的事务级别定义为PROPAGATION_REQUIRED, 那么由于执行ServiceA.methodA的时候, ServiceA.methodA已经起了事务,这时调用ServiceB.methodB,ServiceB.methodB看到自己已经运行在ServiceA.methodA 的事务内部,就不再起新的事务。而假如ServiceA.methodA运行的时候发现自己没有在事务中,他就会为自己分配一个事务。 这样,在ServiceA.methodA或者在ServiceB.methodB内的任何地方出现异常,事务都会被回滚。即使ServiceB.methodB的事务已经被 提交,但是ServiceA.methodA在接下来fail要回滚,ServiceB.methodB也要回滚由于b_service方法事务监听a_service方法,a_service的连接提交事务后就回收到连接池中,b_service获取方法运行完之后归还连接,b_service获取的连接可能是a_service的准备归返的连接,所以就造成跨线程使用链接问题
解决方法就是在b_service 事务管理设置事务级别 propagation = Propagation.REQUIRES_NEW
@Service@Transactional(value = "transaction", propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)public class b_service { @Autowired private Dao dao; /** * 当事务提交后, 才会真正的执行@TransactionalEventListener配置的Listener, 如果Listener抛异常, 方法返回失 * 败, 但事务不会回滚. */ @TransactionalEventListener(phase = TransactionPhase.AFTER_COMPLETION) public String test(){ dao.save(params); }}
参考资料
https://github.com/alibaba/druid/issues/1429
https://github.com/alibaba/druid/issues/1889
http://timerbin.iteye.com/blog/2332995
https://my.oschina.net/haogrgr/blog/224010
ps:
@TransactionalEventListener, 可以方便在事务周期内处理一些事情, 比如事务提交后触发某一事件.
一个场景就是, 当插入记录提交事务后, 异步发送消息到其他系统, 或本地记录日志等操作, 现在可以通过TransactionalEventListener来做了.
上面的文字解释可能表达得不太好,也有些歧意,如果有更好见解,可以联系私信本人,谢谢