`
bugyun
  • 浏览: 551423 次
社区版块
存档分类
最新评论

Spring @Transactional注解不回滚不起作用无效(转)

 
阅读更多

 

转:https://segmentfault.com/a/1190000014617571

https://www.cnblogs.com/huacw/p/8075143.html

 

引言

对于追求数据强一致性的系统,事务扮演者十分重要的角色.最近在项目中遇到一个事务失效的问题,在此分享给大家。

情景回放

### Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Lock wait timeout exceeded; try restarting transaction
; SQL []; Lock wait timeout exceeded; try restarting transaction; nested exception is com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Lock wait timeout exceeded; try restarting transaction
    at org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator.doTranslate(SQLErrorCodeSQLExceptionTranslator.java:259)
    at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:73)
    at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:73)
    at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:446)
    at com.sun.proxy.$Proxy121.update(Unknown Source)
    at org.mybatis.spring.SqlSessionTemplate.update(SqlSessionTemplate.java:294)
    at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:62)
    at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:59)

问题分析

初步分析这是事务获取锁超时导致的错误,奇怪的是抛出异常但是事务没有回滚。或许你们说MySQLTransactionRollbackException是检查性异常(@Transactional默认只捕获非检查性异常),但是项目添加了注解:@Transactional(rollbackFor = Exception.class)。唯一的解释是——事务失效了。

 ProductService.java
/**********************************************************************/
public interface ProductService{
    Integer getPrice(ProductInfo p);
    Integer compute(ProductInfo p);
}
/**********************************************************************/


ProductServiceImpl.java
/**********************************************************************/
@Service
public class ProductServiceImpl implements ProductService{

    public Integer getPrice(ProductInfo p){
        ...
        compute(p);
        ...
    }

    @Transactional(rollbackFor = Exception.class)
    public Integer compute(ProductInfo p){ //TestService的普通方法
        try{
            ...
        }catch(Exception e){
             e.printStackTrace();
             return -1;
        }
    }
}
/**********************************************************************/

初看这段代码,没啥毛病啊。噢,不对,compute 方法内部catch了异常,spring aop无法捕获异常。如果需要捕获异常,需要手动回滚,于是compute方法修改如下:

    @Transactional(rollbackFor = Exception.class)
        public Integer compute(ProductInfo p){ //TestService的普通方法
            try{
                ...
            }catch(Exception e){
                 e.printStackTrace();
                 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();//手动回滚事务
                 return 0;
            }
        }

继续运行,结果发现事务还是未生效。通过查询资料发现,service方法直接调用了本类的一个方法(没有通过接口调用),该方法上的事务将不会生效。

解决方案

想启用本类的普通方法的事务,通过接口来调用该方法即可生效。如果先在方法内部catch异常,需要添加TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();否则可以在外面捕获这个异常。下面是在方法内部捕获异常的示例:

 ProductService.java
/**********************************************************************/
public interface ProductService{
    Integer getPrice(ProductInfo p);
    Integer compute(ProductInfo p);
}
/**********************************************************************/


ProductServiceImpl.java
/**********************************************************************/
@Service
public class ProductServiceImpl implements ProductService{
    @Autowired
    private ProductService productService;
    
    public Integer getPrice(ProductInfo p){
        productService.compute(p);
    }

    @Transactional(rollbackFor = Exception.class)
    public Integer compute(ProductInfo p){ 
        try{
            ...
        }catch(Exception e){
             e.printStackTrace();
             TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
             return 0;
        }
    }
}
/**********************************************************************/

总结

Spring Transactional一直是RD的事务神器,但是如果用不好,反会伤了自己。下面总结@Transactional经常遇到的几个场景:

@Transactional 加于private方法, 无效
@Transactional 加于未加入接口的public方法, 再通过普通接口方法调用, 无效
@Transactional 加于接口方法, 无论下面调用的是privatepublic方法, 都有效
@Transactional 加于接口方法后, 被本类普通接口方法直接调用, 无效
@Transactional 加于接口方法后, 被本类普通接口方法通过接口调用, 有效
@Transactional 加于接口方法后, 被它类的接口方法调用, 有效
@Transactional 加于接口方法后, 被它类的私有方法调用后, 有效

Transactional是否生效, 仅取决于是否加载于接口方法, 并且是否通过接口方法调用(而不是本类调用)。

如果大家有更好的方法,欢迎加入讨论!

分享到:
评论

相关推荐

    Spring @Transactional注解失效解决方案

    Spring @Transactional 注解失效解决方案 ...通过了解 @Transactional 注解的特性和事务传播模式,并遵循解决方案,我们可以解决 @Transactional 注解不回滚的问题,确保事务管理的正确性和可靠性。

    Spring中@Transactional事务回滚(含实例

    需要注意的是,`@Transactional`注解只能对Spring管理的bean起作用,对于非Spring管理的对象(如静态方法或第三方库的代码),`@Transactional`将无法控制事务。 六、异常处理与事务回滚 理解异常处理与事务回滚的...

    Spring中的@Transactional事物回滚实例源码

    在Spring框架中,`@Transactional`注解是事务管理的核心组件,它允许开发者在方法级别声明事务边界。本文将深入探讨这个注解的工作原理、如何配置以及如何在遇到异常时触发事务回滚。 首先,`@Transactional`是...

    Spring3事务管理——使用@Transactional 注解.rar

    - `@Transactional`注解仅在Spring AOP代理能够拦截到的方法上生效,因此,如果在非Spring管理的类或静态方法中使用,事务管理将不起作用。 - 如果事务属性设置不当,可能会导致数据不一致或并发问题,应谨慎调整...

    浅谈Spring中@Transactional事务回滚及示例(附源码)

    最后,在业务逻辑中使用@Transactional注解: ```java @Transactional public void deleteDepartment(Long departmentId) { // 删除部门 departmentDAO.delete(departmentId); // 删除成员 memberDAO....

    后端 Java Spring Data Jpa @Transactional 介绍

    在Java后端开发中,Spring框架提供了强大的事务管理能力,特别是在使用Spring Data JPA时,`@Transactional`注解使得事务处理变得简单易用。这个注解是Spring框架中的核心部分,它允许开发者声明性地控制事务边界,...

    spring的@Transactional注解详细用法1

    总的来说,Spring的`@Transactional`注解为开发者提供了方便且强大的声明式事务管理能力。它简化了事务控制,让开发者能够专注于业务逻辑,而无需关心事务的细节。尽管编程式事务管理可以提供更细粒度的控制,但声明...

    spring的@Transactional注解用法解读

    【Spring的@Transactional注解用法解读】 事务管理是企业级应用程序中的关键部分,它确保了在发生异常时数据的一致性。Spring框架提供了一种统一的方式来处理事务管理,包括对不同事务API(如JTA、JDBC、Hibernate...

    springboot中事务管理@Transactional的注意事项与使用场景

    2. 默认回滚机制:Spring 基于注解的声明式事物 @Transactional 默认情况下只会对运行期异常(java.lang.RuntimeException 及其子类)和 Error 进行回滚。 3. 数据库引擎支持:数据库引擎要支持事务,使用 InnoDB。 ...

    Java注解@Transactional事务类内调用不生效问题及解决办法

    但是,如果在同一个类中的其他方法调用有@Transactional注解的方法时,Spring不会生成代理对象,导致事务不生效。 例如,以下代码中,dosome()方法上有@Transactional注解,但是如果在action()方法中调用dosome(),...

    什么情况会导致@Transactional事务失效?

    3. **静态方法**:由于静态方法不属于任何实例,Spring AOP无法通过代理方式对它们进行拦截,因此`@Transactional`注解不会对静态方法起作用。 4. **异常处理不当**:`@Transactional`依赖于异常传播来决定事务是否...

    spring 简单实例 事务回滚

    Spring默认只在遇到未检查异常时自动回滚事务,如果需要在特定检查异常下回滚,可以在`@Transactional`注解中指定`rollbackFor`属性。 5. 测试:创建一个测试类,模拟一个业务流程,使得在事务中发生异常,观察事务...

    spring 自定义事务管理器,编程式事务,声明式事务@Transactional使用

    **声明式事务管理** 是通过在方法上添加`@Transactional`注解,让Spring自动管理事务。这种方式更加简洁,降低了代码的复杂性。`@Transactional`可以设置不同的属性,如`propagation`(传播行为)、`isolation`...

    注解实现声明式事务管理

    当带有`@Transactional`注解的方法被调用时,Spring会创建一个代理,这个代理会在方法执行前后自动处理事务的开始、提交或回滚。 总之,Spring的注解式声明事务管理极大地简化了事务的管理,使得开发者可以专注于...

    带有@Transactional和@Async的循环依赖问题

    在Spring框架中,`@Transactional` 和 `@Async` 是两个非常重要的注解,它们分别用于声明事务管理和异步执行。然而,当这两个注解同时出现在一个方法上时,可能会引发一些复杂的问题,特别是在存在循环依赖的情况下...

    Spring事务管理A方法内部调用B方法的回滚问题测试代码

    这将扫描带有@Transactional注解的方法,并自动管理它们的事务边界。 ```xml <!-- ... --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> ...

    Spring Data JPA系列4——Spring声明式事务处理与多数据源支持.doc

    @Transactional 注解是 Spring 框架提供的用于标注事务处理的注解。它可以应用于类或方法上,用于指定事务的处理方式。 @Transactional 注解有多种配置项,例如 isolation、propagation、rollbackFor 等,用于指定...

    spring mvc+hibernate 实现事务管理(全注解版)

    5. **异常与事务回滚**: 如果在@Transactional注解的方法中抛出未检查异常(继承自RuntimeException的异常)或者在@Transactional注解中明确列出的检查异常,Spring会自动回滚事务。否则,事务将在方法正常结束时...

    基于Spring JDBC的事务管理

    本资源主要介绍基于Spring JDBC的事务管理,包括事务的定义、使用 @Transactional 注解、Spring JDBC的事务管理机制、事务的ACID特性、事务的传播、事务的隔离等内容。 事务的定义 事务是一种数据库中能够保证一...

Global site tag (gtag.js) - Google Analytics