`

Spring 事务管理注意点

 
阅读更多

在spring中,事务配置可以用注解的方式@Transactional,但是要想要真正发挥事务作用,还需要在配置文件中加如下配置:

<bean  
    id="transactionManager"  
    class="org.springframework.jdbc.datasource.DataSourceTransactionManager"  
    p:dataSource-ref="dataSource" />  
<tx:annotation-driven  
    transaction-manager="transactionManager" /> 

 

一、关于目标对象内部方法自我调用时的一些情形和存在的问题

情形一:只给b方法上加事务注解,a方法不加

public interface AService {
    public void a();

    public void b();
}

@Service()
public class AServiceImpl implements AService{
    public void a() {
        this.b();
    }

    @Transactional(rollbackFor={Exception.class})
    public void b() {
       insert();
       update();
    }
}

只要给目标类AServiceImpl的某个方法加上注解@Transactional,spring就会为目标类生成对应的代理类,以后调用AServiceImpl中的所有方法都会先走代理类(即使调用未加事务注解的方法a,也会走代理类),即在通过getBean("AServiceImpl")获得的业务类时,实际上得到的是一个代理类,假设这个类叫做AServiceImplProxy ,spring为AServiceImpl生成的代理类类似于如下代码:

public class AServiceImplProxy implements AService{
    public void a() {
      // 反射调用目标类的a方法
    }

    public void b() {
      // 启动事务的代码

      // 反射调用目标类的b方法

      // 事务提交的代码
    }
}

由于目标类中只有b方法加入了事务管理,所以代理类中只为b方法加入了横切事务逻辑,spring事务管理的本质是通过aop为目标类生成动态代理类,并在需要进行事务管理的方法中加入事务管理的横切逻辑代码(如AServiceImplProxy中的b方法所示)。

调用getBean("AServiceImpl").a()时,实际上执行的是AServiceImplProxy.a(),代理类的a方法会通过反射调用目标类的a方法, 再在目标类的a方法中调用b方法,故最终a中调用的b方法是来自于AServiceImpl中的b方法,AServiceImpl的b方法并没有横切事务逻辑代码(切记:事务逻辑代码在代理类中,@Transactional只是标记此方法在代理类中要加入事务逻辑代码)。所以调用a方法时,b方法的事务会失效。

 

其实,在proxy对象与目标对象之间还有一个InvocationHandler对象(以jdk动态代理为例),真正的横切逻辑是放到InvocationHandler对象中的,调用逻辑分离到InvocationHandler中主要是为了构造出具有通用性和简单性的代理类,此处为了简化处理过程,统一放到代理对象中来说明,动态代理简化的调用关系图如下:

 

aop中存在方法嵌套调用时,相应的调用过程序列图如下:

 

情形二:给a方法加事务注解,b方法上加或不加

@Service()
public class AServiceImpl implements AService{
   @Transactional(rollbackFor={Exception.class})
   public void a() {
      this.b();
   }

  @Transactional(rollbackFor={Exception.class})
  public void b() {
      insert();

      update();
  }
}
此时生成的代理类类似如下代码:
public class AServiceImplProxy implements AService{
    public void a() {
        // 启动事务的代码

        // 反射调用目标类的a方法
 
        // 事务提交的代码
    }

    public void b() {
        // 启动事务的代码

       // 反射调用目标类的b方法

       // 事务提交的代码
    }
}
即为a和b都加入了事务横切逻辑。在这种情况下,调用顺序还和1中情形类似,区别在于在反射调用目标对象的a方法前,会对a方法开启事务管理,虽然调用的b方法还是目标对象中没有加事务逻辑的代码,spring却会把b合并到a的事务中去,此时相当于只有一个事务。

 

如果再将目标类代码改为:

@Service()
public class AServiceImpl implements AService{
    @Transactional(rollbackFor={Exception.class})
    public void a() {
       this.b();
    }

    public void b() {
       insert();

       update();
    }
}
即只在a上加事务控制,由于b会合并到a的事务中,所以b中的逻辑也可以被事务管理。

由于a和b都合并到了a的事务中,所以这种情形下事务传递规则不适用。代理类中加了事务逻辑的b方法永远不会被调用。

 

那么问题来了,如果我想让b也执行自己的事务逻辑,即调用b时执行代理类中b方法的事务逻辑,该怎么办?

修改目标类中的a方法:

@Transactional(rollbackFor={Exception.class})
public void a() {
    ((AService) AopContext.currentProxy()).b();

    // 即调用AOP代理对象的b方法即可执行事务切面进行事务增强
}
这时,就会强制要求调用代理类中的b方法,从而开启b上的事务,此时b事务上标注的事务传递规则也就可以生效了。

个人觉得这种方法不太好,会污染业务逻辑代码,使代码变复杂。

 

还有一种办法就是接口下沉,把b方法分离到另一个接口中,从根源上避免目标对象内部方法自我调用。

 

二、try catch 的问题

有时需要在业务逻辑代码中显式try catch包裹事务代码,以便在出现异常时进行一些别的处理。

public interface AService {
    public void a();
}

@Service()
public class AServiceImpl implements AService{
    @Transactional(rollbackFor={Exception.class})
    public void a() {
        try {
            insert();
            update();
        } catch (Exception e) {
        }
    }
}
自己在代码中显式捕获异常会导致spring事务回滚失效,原因:spring事务是通过aop捕获到异常后再执行回滚,如果业务代码中显式捕获了异常,会导致spring捕获不到,回滚自然失败。

 

有如下几种解决办法:

(1)业务代码catch住异常后重新抛出

public void a() throws Exception{
     try {
         insert();

         update();
     } catch(Exception e) {
         throw new Exception(e);
     }
}
不足是本方法的调用端也必须显式捕获异常。

 

(2)使用编程式事务显式回滚

public void a() {
     try{
         insert();

         update();
     } catch(Exception e) {
         // 显式回滚
         TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
     }
}
不足是事务控制代码会侵入业务代码,也正是因为编程式事务管理会侵入业务逻辑代码,所以才有了申明式事务管理。

 

 

(3)接口下沉,将需要事务控制的代码分到另一个接口方法中

public interface BService {
    public void a();

    public void b();
}

@Service()
public class BServiceImpl implements BService{
    @Transactional(rollbackFor={Exception.class})
    public void b() {
        insert();

        update();
    }
}
相应的调用端a方法中变为:
public void a() throws Exception{
    try {
        bService.b();
    } catch(Exception e) {
        throw new Exception(e);
    }
}

 

转:https://yq.aliyun.com/articles/64360?utm_campaign=wenzhang&utm_medium=article&utm_source=QQ-qun&utm_content=m_7762

 

分享到:
评论

相关推荐

    Spring事务管理失效原因汇总

    标题“Spring事务管理失效原因汇总”指出了本文的核心内容是分析在使用Spring框架进行事务管理时可能遇到的问题及其原因。描述部分进一步说明了事务失效的后果往往不明显,容易在测试环节被忽略,但在生产环境中出现...

    spring事务详解

    在实际应用中,Spring事务管理的配置和使用需要注意以下几个方面: - 事务传播行为:定义了事务在遇到已有事务时的行为模式,例如是否加入当前事务、新建一个事务等。 - 事务隔离级别:定义了事务的隔离水平,例如...

    Spring事务处理-ThreadLocal的使用

    首先,了解Spring事务管理的基本概念。在多线程环境中,事务管理是至关重要的,它负责确保一组数据库操作要么全部成功,要么全部失败。Spring提供了两种主要的事务管理方式:编程式事务管理和声明式事务管理。声明式...

    spring 注解事务管理

    Spring事务管理主要分为两种方式:编程式事务管理和声明式事务管理。编程式事务管理是通过编写代码来控制事务的开始、提交、回滚等操作,而声明式事务管理则是通过配置或注解来定义事务边界,更加直观和易于使用。 ...

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

    在Spring框架中,事务管理是核心特性之一,用于确保数据操作的一致性和完整性。当一个方法(A方法)内部调用另一个方法(B方法)时,可能会遇到事务控制...这个示例代码对于理解和调试Spring事务管理的问题非常有帮助。

    Spring事务详解

    本文将深入探讨Spring事务管理的概念、类型、配置方式以及在实际开发中的应用。 首先,我们要理解什么是事务。事务是数据库操作的基本单元,它确保一组数据库操作要么全部成功,要么全部失败。事务有四大特性,即...

    spring 事务基于注解模式

    Spring事务管理分为编程式和声明式两种。编程式事务管理通过编程的方式(如使用`TransactionTemplate`或直接调用`PlatformTransactionManager`)来控制事务的开始、提交、回滚等操作。而声明式事务管理则是在配置...

    springtransaction 事务管理

    Spring事务管理主要包括编程式事务管理和声明式事务管理两种方式,使得开发者能够在不深入理解底层事务实现的情况下,轻松地在应用程序中管理事务。 1. **编程式事务管理**: 编程式事务管理是通过调用`...

    spring事务管理.rar

    《Spring事务管理详解》 在Java企业级应用开发中,数据一致性是至关重要的,而Spring框架的事务管理机制就是为了解决这个问题。本教程将深入探讨Spring中的事务管理,包括其核心概念、配置方式以及实战应用。 首先...

    Spring自定义切面事务问题

    在开发基于Spring框架的应用程序时,我们经常需要利用AOP(面向切面编程)来实现横切关注点(如日志记录、安全控制、事务管理等)的模块化处理。其中,事务管理是保证数据一致性的重要手段之一。然而,在实际开发...

    Hibernate缓存与spring事务详解

    **二、Spring事务管理** 1. **编程式事务管理** - 通过`PlatformTransactionManager`接口和`@Transactional`注解实现。手动调用`TransactionTemplate`或在方法上添加注解来开启和提交事务,适用于简单的事务处理。 ...

    SpringBoot事务和Spring事务详讲

    虽然 Spring 和 Spring Boot 提供了强大的事务管理支持,但在实际开发中还需要注意以下几点: - **异常处理**:确保事务边界内的异常被正确捕获和处理,否则可能导致事务未能按预期回滚。 - **并发控制**:在多线程...

    JavaEE spring事务操作环境和基本功能搭建

    在JavaEE开发中,Spring框架是广泛使用的工具之一,它为开发者提供了许多便利,包括对事务管理的强大支持。本文将详细讲解如何搭建Spring事务操作环境以及实现基本功能。 首先,理解事务(Transaction)的重要性。...

    Spring声明式事务配置管理方法

    Spring 声明式事务管理是Spring框架中的一个重要特性,它允许开发者在不编写任何事务管理代码的情况下,通过配置来管理事务。这种方式极大地简化了事务处理,并提高了代码的可维护性。以下是关于Spring声明式事务...

    springboot整合spring事务

    而Spring事务管理是其核心特性之一,它为开发者提供了强大的事务处理能力,确保数据的一致性和完整性。本篇文章将深入探讨如何在Spring Boot中整合Spring事务管理,并详细阐述其关键概念和技术细节。 首先,了解...

    spring基于AspectJ实现事务控制

    AspectJ是一种面向切面编程(AOP)的Java语言扩展,它允许开发者定义“切面”——关注点的模块化,这些关注点包括日志、事务管理、安全性等。AspectJ提供了一种强大的类型系统和编译时及运行时织入机制,使得切面...

    Spring Nested事务简单案例

    在Spring框架中,事务管理是核心功能之一,它允许开发者以声明式或编程式的方式处理事务边界。在本案例中,我们关注的是Spring中的Nested事务,这是一个相对复杂的特性,但非常有用,特别是在需要子事务处理的场景下...

    Spring事务配置的五种方式

    Spring 事务配置是Spring框架中不可或缺的部分,它用于管理和协调应用程序中的事务边界,确保数据的一致性和完整性。...在使用过程中,还需注意事务的隔离级别、传播行为、回滚规则等事务特性,确保事务管理正确无误。

    spring 事务传播

    ### Spring 事务传播属性详解 #### 一、Spring 事务基础概述 在深入探讨Spring框架中的事务传播属性...此外,需要注意的是,在实际开发中,还应结合事务的隔离级别、读写锁等其他高级特性来进一步优化事务管理策略。

    spring事务管理

    Spring事务管理分为编程式事务管理和声明式事务管理两种方式,这两种方式各有优缺点,适用于不同的场景。 编程式事务管理允许开发者在代码中显式地开始、提交、回滚事务。通过`PlatformTransactionManager`接口和`@...

Global site tag (gtag.js) - Google Analytics