zoukankan      html  css  js  c++  java
  • Spring中同一个service中方法相互调用事务不生效问题解决方案

    问题描述:

    我们在用Spring框架开发Web项目过程中,经常需要用同一个service中的一个方法调用另一个方法,如果此时调用方没有添加事务注解@Transactional,而在被调用方添加事务注解@Transactional,当被调用方法中出现异常,这时候会发现事务并没有回滚,事务注解@Transactional没有起作用。

    分析原因:

    我们知道Spring中事务管理是使用AOP代理技术实现的,目标对象自身并没有事务管理功能的,而是通过代理对象动态增强功能对事务进行增强的。因此当我们在同一个service类中通过一个方法调用另一个方法时,是通过目标对象this对象调用的,目标对象自身并没有事务管理功能,因此事务不能生效。

    下面我们用代码演示下:

    1 public class UserService{
    2     ...
    3     public User getUserByName(String name) {
    4        return userDao.getUserByName(name);
    5     }
    6     ...

    如果配置了事务, 就相当于又创建了一个类:

     1 public class UserServiceProxy extends UserService{
     2     private UserService userService;
     3     ...
     4     public User getUserByName(String name){
     5         User user = null;
     6         try{
     7             // 在这里开启事务
     8             user = userService.getUserByName(name);
     9             // 在这里提交事务
    10         }
    11         catch(Exception e){
    12             // 在这里回滚事务
    13 
    14             // 这块应该需要向外抛异常, 否则我们就无法获取异常信息了. 
    15             // 至于方法声明没有添加异常声明, 是因为覆写方法, 异常必须和父类声明的异常"兼容". 
    16             // 这块应该是利用的java虚拟机并不区分普通异常和运行时异常的特点.
    17             throw e;
    18         }
    19         return user;
    20     }
    21     ...
    22 }
    1 @Autowired
    2 private UserService userService;    // 这里spring注入的实际上是UserServiceProxy的对象
    3  
    4 private void test(){
    5     // 由于userService是UserServiceProxy的对象, 所以拥有了事务管理的能力
    6     userService.getUserByName("aa");
    7 }

    Spring事务失效的其他原因

    通过对Spring事务代理模式的分析,我们不难发现Spring事务失效的原因有以下几种情况:

    1.private、static、final的使用

    解决方法:不在类和方法上使用此类关键字

    2.通过this.xxx(调用当前类的方法)

    使用xml配置方式暴露代理对象.然后在service中通过代理对象AopContext.currentProxy()去调用方法。

    xml配置
    <aop:aspectj-autoproxy proxy-target-class="true" expose-proxy="true"/>
    service调用
     1 @Service
     2 public class HelloWorldServiceImpl implements HelloWorldService {
     3     @Autowired
     4     private BlogRepository blogRepository;
     5 
     6     @Override
     7     public void a(BlogEntity blogEntity) throws Exception {
     8         ((HelloWorldService) AopContext.currentProxy()).b(blogEntity);
     9     }
    10 
    11     @Transactional(rollbackFor = Exception.class)
    12     @Override
    13     public void b(BlogEntity blogEntity) throws Exception {
    14         blogRepository.save(blogEntity);
    15         throw new Exception("错误");
    16     }
    17 }

    3.使用默认的事务处理方式

    spring的事务默认是对RuntimeException进行回滚,而不继承RuntimeException的不回滚。因为在java的设计中,它认为不继承RuntimeException的异常是”checkException”或普通异常,如IOException,这些异常在java语法中是要求强制处理的。对于这些普通异常,spring默认它们都已经处理,所以默认不回滚。可以添加rollbackfor=Exception.class来表示所有的Exception都回滚。

    4.线程Thread中声明式事务不起作用

     1 @Override
     2     public void run() {
     3         DefaultTransactionDefinition def = new DefaultTransactionDefinition();
     4         def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
     5         PlatformTransactionManager txManager = ContextLoader.getCurrentWebApplicationContext().getBean(PlatformTransactionManager.class);
     6         TransactionStatus status = txManager.getTransaction(def);
     7         try {
     8                testDao.save(entity);
     9                txManager.commit(status); // 提交事务
    10             } catch (Exception e) {
    11                 System.out.println("异常信息:" + e.toString());
    12                 txManager.rollback(status); // 回滚事务
    13             }                        
    14     }
    
    

    从上面代码可以看出,我们的解决方案是使用了编程式事务。

    5.配置的事务与扫描的service不在同一个容器

     在spring-framework-reference.pdf文档中有这样一段话:

    <tx:annotation-driven/> only looks for @Transactional on beans in the same application context it is defined in. This means that, if you put <tx:annotation-driven/> in a WebApplicationContext for a DispatcherServlet, it only checks for @Transactional beans in your controllers, and not your services. 

        这句话的意思是,<tx:annoation-driven/>只会查找和它在相同的应用上下文件中定义的bean上面的@Transactional注解,如果你把它放在Dispatcher的应用上下文中,它只检查控制器上的@Transactional注解,而不是你services上的@Transactional注解。

        如果将事务配置定义在Spring MVC的应用上下文(*-servlet.xml)中,在Controller上的@Transactional注解是可以起作用的。而services上的@Transactional注解将不起作用。

    详情可见:https://www.cnblogs.com/xiaojiesir/p/11058541.html

    6.方法配置的事务传播行为有问题

    被调用方法的事务传播行为设置为PROPAGATION_REQUIRES_NEW,导致产生两个独立的事务,外围方法抛出异常只回滚和外围方法同一事务的方法。

    详情可见:https://segmentfault.com/a/1190000013341344

  • 相关阅读:
    SPOJ
    hdu1298(字典树)
    hdu1247(字典树)
    hdu1075(字典树)
    Redisson教程
    Redisson官方文档
    Springboot 防止XSS攻击,包含解决RequestBody 的Json 格式参数
    防止XSS脚本注入-前端vue、后端springboot
    在Intellij IDEA中使用Debug
    使用Hibernate-Validator优雅的校验参数
  • 原文地址:https://www.cnblogs.com/xiaojiesir/p/11089142.html
Copyright © 2011-2022 走看看