@Transactional使用不当导致数据库断开连接connection disable

本文描述了一个由于在不适当的地方使用事务而导致数据库连接超时的问题,并给出了具体的解决方案。涉及事务管理、数据库连接池配置及事务生命周期管理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

事务使用不当的坑,踩过不止一个两个了,只是,柳暗花明又一坑~

 

某个项目用的mysql数据库和druid连接池,在测试环境经常报connection disable,如下:

 

       相信大部分人能一眼看出,这是由于数据库主动断开了某个连接,而程序还在继续使用该connection。由报错信息可知,mysql服务器的wait_timeout只有60s,简直离谱!正常来说其默认的超时时间应为8小时。如报错建议,第一反应是调整mysql超时时间,但是该数据库属于其他开发商,无法修改超时配置。

      但是什么样的数据库操作会耗时1分多钟呢?分析方法具体代码

 

操作如下:

1、先查询某条记录;

2、调用第三方接口推送该记录;

3、把推送结果插入日志表;

     代码是没有问题的,那会是连接池没有设置testWhileIdle为true的原因?

配置是没问题的,这样会在获取空闲连接的时候先测试该连接是否正常。

 

没办法,只能走一遍源码,然后就发现这个方法居然开启了事务!

 

 

原来是直接在类上打了@transactional,那基本能确定问题原因了,使用事务,同一个数据源必定使用同一个connection,事务开启的时候,事务管理器会把当前的connection放进ThreadLocal里,所有在事务里的数据库操作都会通过DataSourceUtil获取该connection。

 

        所以步骤1查询的时候会把当前的connection放进ThreadLocal,然后步骤2如果耗时超过60s的话,步骤3通过ThreadLocal获取的connection必然是超时的,这样就导致了步骤2即使在第61s成功返回结果(虽然很过分,但理论上是可能的),步骤3也会报错,根本无法记录日志。

       解决方法也很简单,上面的操作根本不需要事务,把@transactional去掉就行了,这样在步骤3的时候会获取新的connection(且通过validationQuery验证有效性)。

       实际开发过程中,出现这种问题的几率应该是比较小的(除非如上,数据库超时时间设置足够小,事务内操作耗时足够久),但是这种编码习惯有待商榷,直接给service类声明事务,尽管方便,但如果不能保证该类在拓展方法的时候是单纯持久层操作,还是谨慎为上,事务应该只在有需要的时候才开启。

 

 

 

### 回答1: 如果一个方法使用了@Transactional注解,但是其内部的一些数据库操作不回滚,这是因为事务可能已经被标记为回滚,或者存在抛出异常导致事务回滚。你可以捕获这个异常并在代码中进行处理,以避免事务回滚。此外,您也可以通过使用 try-catch 块并在 catch 块中手动回滚事务来解决此问题。 ### 回答2: 在Spring框架中,@Transactional注解用于标记一个方法开启事务处理。当一个方法被注解为@Transactional时,Spring会在该方法执行之前开启一个事务,在方法执行结束时根据方法的执行结果选择性地提交或回滚事务。 然而,@Transactional注解默认只对未经检查的异常进行回滚,对于检查的异常(即被声明为throws的异常)或者在方法内部捕获并处理的异常,默认是不会回滚事务的。因此,如果在方法内部存在数据库操作失败但未抛出未经检查的异常,事务将不会回滚。 为了解决这个问题,可以使用rollbackFor属性来指定需要回滚的异常类型。例如,如果想要在方法内部遇到任何异常时都回滚事务,可以使用@Transactional(rollbackFor = Exception.class)。 另外,在某些情况下,如果方法在调用其他带有@Transactional注解的方法时,对于内部的数据库操作是否回滚也会受到影响。如果调用的方法本身已经开启了事务并成功提交,那么调用方法的事务也不会回滚,即使调用方法内部的数据库操作失败。 总之,@Transactional注解在方法内部对于数据库操作是否回滚有一些注意事项。要确保在需要回滚的情况下正确地使用异常处理和设置rollbackFor属性,以便实现事务的正确回滚。 ### 回答3: @TransactionaI注解是Spring框架中的事务注解,用于管理事务。当一个方法被@Transactional注解标记时,Spring会为该方法开启一个事务,如果方法执行过程中发生异常,则事务会回滚,将数据库状态恢复到方法执行前的状态。但是,有时我们可能需要在一个方法内部执行多个数据库操作,并且只希望其中一部分操作发生异常时回滚,而不是全部回滚。 为了实现这个需求,我们可以在方法内部手动控制事务边界,使用编程式事务管理。首先,需要在方法上添加@Transactional(rollbackFor = Exception.class)注解来声明一个事务,并指定需要回滚的异常类型。 接下来,我们可以通过编写try-catch语句块来捕获可能发生异常的代码块,并在catch块中执行回滚操作。具体步骤如下: 1. 方法开始前,使用PlatformTransactionManager的getTransaction()方法获取当前事务。 2. 调用TransactionStatus的beginTransaction()方法来开始事务,并将返回的事务状态保存到局部变量中。 3. 在try语句块中执行需要事务管理的数据库操作。 4. 如果发生异常,将事务状态设置为回滚状态。 5. 最后如果存在事务,通过TransactionStatus的commit()或rollback()方法提交或回滚事务。 这样,我们就可以控制事务的范围并选择性地回滚异常。但是需要注意的是,手动控制事务会增加代码复杂性,并且需要确保在事务处理期间不会出现竞争条件或不一致的状态。 总之,@Transactional注解是用于管理事务的注解,通过编程式事务管理可以实现方法内部部分数据库操作不回滚的需求。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值