Spring事务

spring事务

spring事务与数据库事务的区别:

spring的事务是对数据库的事务的封装 最后本质的实现还是在数据库 数据库的事务说简单就只有开启,回滚和关闭,spring对数据库事务的包装,原理就是拿一个数据连接,根据spring的事务配置,操作这个数据连接对数据库进行事务开启,回滚或关闭操作.但是spring除了实现这些,还配合spring的传播行为对事务进行了更广泛的管理.其实这里还有个重要的点,那就是事务中涉及的隔离级别,以及spring如何对数据库的隔离级别进行封装.事务与隔离级别放在一起理解会更好些.

务的结果被写到持久化存储器中。

Spring事务工作原理

1)事务开始时,通过AOP机制,生成一个代理的connection对象,并将其放入DataSource实例的某个与DataSourceTransactionManager相关的容器中。在接下来的整个事务中,客户端与数据库的交互都使用该connection。

2)事务结束时,回滚在第一步中得到的代理的connection中的数据库命令,然后关闭该代理connection对象。

spring事务管理机制

Spring 框架中,最重要的事务管理的 API 有三个:TransactionDefinition、PlatformTransactionManager 和 TransactionStatus。 所谓事务管理,实质上就是按照给定的事务规则来执行提交或者回滚操作。其中,“给定的事务规则”是用 TransactionDefinition 表示的,“按照……来执行提交或者回滚操作”是用 PlatformTransactionManager 表示的,而 TransactionStatus 可以看作代表事务本身。

  1. PlatformTransactionManager 接口 事务管理器,主要用于平台相关的事务管理。

    Spring事务策略是通过PlatformTransactionManager接口体现的,该接口是Spring事务策略的核心。该接口的源代码如下:

    public interface PlatformTransactionManager {
        //平台无关的获得事务的方法
        TransactionStatus getTransaction(TransactionDefinition definition) throws        TransactionException;
        //平台无关的事务提交方法
        void commit(TransactionStatus status) throws TransactionException;
        //平台无关的事务回滚方法
        void rollback(TransactionStatus status) throws TransactionException;
    }
    
  2. TransactionDefinition 接口 事务定义信息(隔离级别、传播、超时、只读)通过配置如何进行事务管理。

    用于定义一个事务的规则,它包含了事务的一些静态属性,比如:事务传播行为、超时时间等。同时,Spring 还为我们提供了一个默认的实现类:DefaultTransactionDefinition,该类适用于大多数情况。如果该类不能满足需求,可以通过实现 TransactionDefinition 接口来实现自己的事务定义。

    public interface TransactionDefinition {
        int PROPAGATION_REQUIRED = 0;
        int PROPAGATION_SUPPORTS = 1;
        int PROPAGATION_MANDATORY = 2;
        int PROPAGATION_REQUIRES_NEW = 3;
        int PROPAGATION_NOT_SUPPORTED = 4;
        int PROPAGATION_NEVER = 5;
        int PROPAGATION_NESTED = 6;
        int ISOLATION_DEFAULT = -1;
        int ISOLATION_READ_UNCOMMITTED = 1;
        int ISOLATION_READ_COMMITTED = 2;
        int ISOLATION_REPEATABLE_READ = 4;
        int ISOLATION_SERIALIZABLE = 8;
        int TIMEOUT_DEFAULT = -1;
    //、事务传播行为、、事务的只读属性和事务的回滚规则
        int getPropagationBehavior();
    //事务隔离级别
        int getIsolationLevel();
    //事务超时
        int getTimeout();
    //事务的只读属性
        boolean isReadOnly();
    
        String getName();
    }
    //参考 spring事务的7大传播特性 5大隔离级别
    

    事务超时

    **所谓事务超时,就是指一个事务所允许执行的最长时间,如果超过该时间限制但事务还没有完成,则自动回滚事务。**在 TransactionDefinition 中以 int 的值来表示超时时间,其单位是秒。

    事务的只读属性

    事务的只读属性是指,对事务性资源进行只读操作或者是读写操作。所谓事务性资源就是指那些被事务管理的资源,比如数据源、 JMS 资源,以及自定义的事务性资源等等。如果确定只对事务性资源进行只读操作,那么我们可以将事务标志为只读的,以提高事务处理的性能。在 TransactionDefinition接口中,以 boolean 类型来表示该事务是否只读。
    事务的回滚规则

    通常情况下,如果在事务中抛出了未检查异常(继承自 RuntimeException 的异常),则默认将回滚事务。如果没有抛出任何异常,或者抛出了已检查异常,则仍然提交事务。这通常也是大多数开发者希望的处理方式,也是 EJB 中的默认处理方式。但是,我们可以根据需要人为控制事务在抛出某些未检查异常时任然提交事务,或者在抛出某些已检查异常时回滚事务。

  3. TransactionStatus 接口 事务具体运行状态——事务管理过程中,每个时间点事务的状态信息。

    PlatformTransactionManager.getTransaction(…) 方法返回一个 TransactionStatus 对象,该对象可能代表一个新的或已经存在的事务(如果在当前调用堆栈有一个符合条件的事务)。

三个接口主要关系

首先用户管理事务,需要先根据选用的ORM持久层框架配置TransactionManager进行事务管理。
然后根据TransactionDefinition(事务定义信息),通过TransactionManager进行事务管理。
事务运行过程中,每个时刻都可通过TransactionStatus来了解事务运行状态。

Spring如何处理事务?2种

Spring 仍然将事务操作委托给底层的持久化框架来执行。

  1. 编程式事务管理**(不常用)**

    可以通过Spring框架提供的TransactionTemplate 通过TransactionTemplate手动管理事务

    public class BankServiceImpl implements BankService {
        private BankDao bankDao;
        private TransactionTemplate transactionTemplate;
        ......
        public boolean transfer(final Long fromId, final Long toId, final double amount) {
            return (Boolean) transactionTemplate.execute(new TransactionCallback(){
                public Object doInTransaction(TransactionStatus status) {
                    Object result;
                    try {
                            result = bankDao.transfer(fromId, toId, amount);
                        } catch (Exception e) {
                            status.setRollbackOnly();
                            result = false;
                            System.out.println("Transfer Error!");
                    }
                    return result;
                }
            });
        }
    }
    
    <bean id="bankService" class="footmark.spring.core.tx.programmatic.template.BankServiceImpl">
        <property name="bankDao" ref="bankDao"/>
        <property name="transactionTemplate" ref="transactionTemplate"/>
    bean>
    

    也可以 直接使用底层的PlatformTransactionManager

    public class BankServiceImpl implements BankService {
        private BankDao bankDao;
        private TransactionDefinition txDefinition;
        private PlatformTransactionManager txManager;
        ......
        public boolean transfer(Long fromId, Long toId, double amount) {
            // 获取一个事务
            TransactionStatus txStatus = txManager.getTransaction(txDefinition);
            boolean result = false;
            try {
                result = bankDao.transfer(fromId, toId, amount);
                txManager.commit(txStatus);    // 事务提交
            } catch (Exception e) {
                result = false;
                txManager.rollback(txStatus);      // 事务回滚
                System.out.println("Transfer Error!");
            }
            return result;
        }
    }
    
    <bean id="bankService" class="footmark.spring.core.tx.programmatic.origin.BankServiceImpl">
        <property name="bankDao" ref="bankDao"/>
        <property name="txManager" ref="transactionManager"/>
        <property name="txDefinition">
        <bean class="org.springframework.transaction.support.DefaultTransactionDefinition">
            <property name="propagationBehaviorName" value="PROPAGATION_REQUIRED"/>
        bean>
        property>
    bean>
    
  2. 声明式事务管理 (交由容器管理事务) (XML配置文件方式或注解方式)

    Spring 的声明式事务管理是建立在 Spring AOP 机制之上通过AOP实现的。

    声明式事务管理使用了AOP面向切面编程实现的,本质就是在目标方法执行前后进行拦截。在目标方法执行前加入或创建一个事务,在执行方法执行后,根据实际情况选择提交或是回滚事务。

    是spring在Aop基础上提供的事务实现机制。最大优点:不需要再业务代码种添加事务管理的代码,只需要在配置文件中做相关的事务规则声明就可以了,但是声明式事务只能针对方法级别,无法控制代码级别的事务管理。

    一般情况下比编程式事务好用。
    将事务管理代码从业务方法中分离出来,以声明的方式来实现事务管理。
    将事务管理作为横切关注点,通过aop方法模块化。Spring中通过Spring AOP框架支持声明式事务
    管理

    使用Spring管理事务,注意头文件的约束导入 : tx

    xmlns:tx="http://www.springframework.org/schema/tx"
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx.xsd">
    

    基于 命名空间的声明式事务管理

    基于@Transactional

    @Transactional只有应用到public方法才会有效,因为在获取事务注解时,会调用AbstractFallbackTransactionAttributeSource的computeTransactionAttribute方法。

    @Transactional 可以作用于接口、接口方法、类以及类方法上:当作用于类上时,该类的所有 public 方法将都具有该类型的事务属性;当作用于方法上时,该标注来覆盖类级别的定义。

    @Transactional(propagation = Propagation.REQUIRED)
    public boolean transfer(Long fromId, Long toId, double amount) {
        return bankDao.transfer(fromId, toId, amount);
    }
    

    Spring 使用 BeanPostProcessor 来处理 Bean 中的标注,因此我们需要在配置文件中作如下声明来激活该后处理 Bean,如下所示:

    <tx:annotation-driven transaction-manager="transactionManager"/>
    

    Spring 声明式事务的本质

    就Spring 声明式事务而言,无论其基于 命名空间的实现还是基于 @Transactional 的实现,其本质都是 Spring AOP 机制的应用:即通过以@Transactional的方式或者XML配置文件的方式向业务组件中的目标业务方法插入事务增强处理并生成相应的代理对象供应用程序(客户端)使用从而达到无污染地添加事务的目的。

    img

spring事务传播机制

所谓事务的传播行为是指,如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为。

Spring中事务定义了不同的传播级别 Propagation

 Propagation propagation() default Propagation.REQUIRED;//默认

A() A调用了B(),那么方法B有无事务以及方法B对事务的要求不同都会对方法A的事务具体执行造成影响,同时方法A的事务对方法B的事务执行也有影响,如何影响具体由两个方法所定义的事务传播类型决定的

  1. required:默认。如果当前(调用方)存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。A没有事务,B就新建一个事务(required修饰B),如果A有事务,B加入。A有事务则B加入

  2. supports:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。

  3. mandatory:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常

  4. requires_new:创建一个新的事务,如果当前存在事务,则把当前事务挂起(B自己以事务运行,A是A的事务只回滚自己。B也只回滚自己)。

  5. not_supported:以非事务方式运行,如果当前存在事务,则把当前事务挂起(B一定以非事务方式执行)。

  6. never:以非事务方式运行,如果当前存在事务,则抛出异常。

  7. nested:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于transactiondefinition.propagation_required。A调B,A没有B自己开启一个事务。A有B嵌套在里面(A父事务B子事务)

    • 与requires_new的区别

      requires_new 新建一个事务并且新事务与原有的事务无关。而nested是当前存在事务时会开启一个嵌套事务,父事务回滚时子事务也会回滚。requires_new原因有事务回滚,不会影响新开启的事务。

    • 与required区别

      required A存在事务,B和A使用用一条事务,B异常,由于共用一个事务,所以无论A是否catch异常,事务都会回滚。而在nested下,B异常,A可以catch其异常,这样具有子事务回滚,A不受影响

主要分为三大类:

Required(默认)、Supports、Mandatory:支持当前事务,A调用B、若A事务存在,那么B和A处于同一个事务。
Requires_New、Not_Supported、Never:不支持原来的事务,A调用B、若A事务存在,那么B和A肯定不处于同一个事务。
Nested:嵌套事务,允许在同一事务设置保存点,回滚保存点。

spring中事务的隔离级别

所谓事务的隔离级别是指若干个并发的事务之间的隔离程度

对数据库来说隔离级别只有4种,Spring多了一个DEFAULT 这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别.

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zGui7Oty-1624893946695)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1624375580583.png)]

  1. default(-1), 默认级别 使用数据库默认的事务隔离级别.
  2. read_uncommitted(1), 该隔离级别表示一个事务可以读取另一个事务修改但还没有提交的数据,该级别不能防止脏读和不可重复读,因此很少使用该隔离级别;
  3. read_committed(2), 该隔离级别表示一个事务只能读取另一个事务已经提交的数据。该级别可以防止脏读,这也是大多数情况下的推荐值。
  4. repeatable_read(4), 可重复读,在同一个事务内,任意一个时刻的查询结果是一致的 。该隔离级别表示一个事务在整个过程中可以多次重复执行某个查询,并且每次返回的记录都相同。即使在多次查询之间有新增的数据满足该查询,这些新增的记录也会被忽略。该级别可以防止脏读和不可重复读。
  5. serializable(8); 所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是,这将严重影响程序的性能,通常情况下也不会用到该级别。

数据库事务隔离级别

  • read-uncommitted 未提交读 最低隔离级别、事务未提交前,就可被其他事务读取(会出现幻读、脏读、不可重复读)。 写数据会锁住整张表

  • read-committed 提交读 一个事务提交后才能被其他事务读取到(会造成幻读、不可重复读)。

  • repeatable-read 可重复读 默认级别, 确保事务可以多次从一个字段中读取相同的值,在此事务持续期间,禁止其他事务对此字段的更新,可以避免脏读和不可重复读,仍会出现幻读问题。 写数据会锁住整张表

  • serializable 序列化 最严格的事务隔离级别,要求所有事务被串行执行,不能并发执行,可避免脏读、不可重复读、幻读情况的发生。 读写数据都会锁住整张表

在Oracle、SqlServer中都是选择读已提交(Read Commited)作为默认的隔离级别,为什么Mysql不选择读已提交(Read Commited)作为默认隔离级别,而选择可重复读(Repeatable Read)作为默认的隔离级别呢?

事务管理器

Spring事务什么时候会失效

spring事务的原理是AOP,进行了切面增强,那么失效的根本原因就是AOP不起作用了。AOP在底层生成一个代理对象。 对需要spring管理事务的bean生成了代理对象,然后通过代理对象拦截了目标方法的执行,在方法前后添加了事务的功能,所以必须通过代理对象调用目标方法的时候,事务才会起效。

  1. 发生自调用,类里面使用ths调用本类的方法,此时this对象不是代理对象,而是UserService对象本身。用this就没有走代理逻辑。解决方案:让那个this编程UserService的代理类即可。使用@Autowire注入

    @Component
    public class UserService {
        public void m1(){
            this.m2();
        }
        
        @Transactional
        public void m2(){
            //执行db操作
        }
    }
    //当外部直接调用m1的时候,m2方法的事务会生效么?显然不会生效
    //因为m1中通过this的方式调用了m2方法,而this并不是代理对象,this.m2()不会被事务拦截器,
    //所以事务是无效的,如果外部直接调用通过UserService这个bean来调用m2方法,事务是有效的,
    //在UserService中注入了自己,此时m1中的m2事务是生效的
    @Component
    public class UserService {
        @Autowired //@1
        private UserService userService;
    
        public void m1() {
            this.userService.m2();
        }
    
        @Transactional
        public void m2() {
            //执行db操作
        }
    }
    

    重点:必须通过代理对象访问方法,事务才会生效

  2. 方法不是public

  3. 数据库不支持事务

  4. 没有被spring管理(类没有放到spring容器中,比如@Service之类的注解)

  5. 异常被吃掉,事务不会回滚(或者抛出的异常没有被定义,默认为RuntimeException)

    spring事务回滚的机制:对业务方法进行try catch,当捕获到有指定的异常时,spring自动对事务进行回滚,那么问题来了,哪些异常spring会回滚事务呢?
    并不是任何异常情况下,spring都会回滚事务,默认情况下,RuntimeException和Error的情况下,spring事务才会回滚。
    也可以自定义回滚的异常类型: @Transactional(rollbackFor = {异常类型列表})

    /**
    当业务方法抛出异常,spring感知到异常的时候,才会做事务回滚的操作,若方法内部将异常给吞了,那么事务无法感知到异常了,事务就不会回滚了。
    如下代码,事务操作2发生了异常,但是被捕获了,此时事务并不会被回滚
    **/
    @Transactional
    public void m1(){
        事务操作1
        try{
            事务操作2,内部抛出了异常
        }catch(Exception e){
            
        }
    }
    
  6. 业务和spring事务代码必须在一个线程中

/**spring事务实现中使用了ThreadLocal,ThreadLocal大家应该知道吧,可以实现同一个线程中数据共享,
 必须是同一个线程的时候,数据才可以共享,
 这就要求业务代码必须和spring事务的源码执行过程必须在一个线程中,才会受spring事务的控制,比如下面代码,   方法内部的子线程内部执行的事务操作将不受m1方法上spring事务的控制,这个大家一定要注意
    **/
@Transactional
public void m1() {
    new Thread() {
        一系列事务操作
    }.start();
}

事务何时创建?何时提交?

createTransactionIfNecessary告诉我们在必要的时候才创建事务

Spring事务回滚

Spring事务管理是根据异常来进行回滚操作; Spring与Mybatis整合时,虽然在Service方法中并没有check异常,但是如果数据库有异常发生,默认会进行事务回滚。 Spring 如果不添加rollbackFor等属性,Spring碰到Unchecked Exceptions都会回滚,不仅是RuntimeException,也包括Error。 如果在事务方法中捕获异常并进行处理,一定要继续抛出异常并在Spring事务管理中进行rollbak-for配置。 @Transactional(rollbackFor = Exception.class)

@Transactional(rollbackFor=Exception.class)

​ 在项目中,@Transactional(rollbackFor=Exception.class), 如果是多个,可以rollbackFor={Exception.class,Exceptions.class} ,如果类加了这个注解,那么这个类里面的方法抛出异常,就会回滚,数据库里面的数据也会回滚。 在@Transactional注解中如果不配置rollbackFor属性,那么Spring只会在遇到RuntimeException的时候才会回滚,加上rollbackFor=Exception.class,可以让事物在遇到非运行时异常时也回滚

这个方法会检查目标方法的修饰符是不是public,若不是public,就不会获取@Transactional的属性信息。最终不会让TransactionInterceptor 来拦截该目标方法进行事务管理。

Spring中的@Transactional基于动态代理的机制,提供了一种透明的事务管理机制

当作用于类时,该类的所有的public方法都将具有事务的特性。但是 Spring 建议不要在接口或者接口方法上使用该注解,因为这只有在使用基于接口的代理时它才会生效。另外,@Transactional注解只能应用到public方法上,作用于protected、private时,会被忽略,也不会抛出任何异常,这是有Spring AOP的本质决定的。

此外,Spring声明式事务默认会对非检查型异常和运行时异常进行回滚,而对检查型异常不进行回滚操作。Error和RuntimeException及其子类为非检查型异常。如何改变默认规则?

1)让checked异常也进行回滚,需要加上(rollbackFor=Exception.class)

2)让unchecked异常也不回滚,加上(notRollbackFor=RunTimeException.class)

3)不需要事务,@Transactional(propagation=Propagation.NOT_SUPPORTED)

4)如果不添加rollbackFor等属性,Spring碰到Unchecked Exceptions都会回滚,不仅是RuntimeException,也包括Error。

Spring使用声明式事务处理,默认情况下,如果被注解的数据库操作方法中发生了unchecked异常,所有的数据库操作将rollback;如果发生的异常是checked异常,默认情况下数据库操作还是会提交的。

checked异常:
表示无效,不是程序中可以预测的。比如无效的用户输入,文件不存在,网络或者数据库链接错误。这些都是 外在的原因,都不是程序内部可以控制的。
必须在代码中显式地处理。比如try-catch块处理,或者给所在的方法加上throws说明,将异常抛到调用栈的上 一层。
继承自java.lang.Exception(java.lang.RuntimeException除外)。

unchecked异常:
表示错误,程序的逻辑错误。是RuntimeException的子类,比如IllegalArgumentException, NullPointerException和IllegalStateException。
不需要在代码中显式地捕获unchecked异常做处理。
继承自java.lang.RuntimeException(而java.lang.RuntimeException继承自java.lang.Exception)。
虽然配置了rollbackFor属性,但是方法执行失败,还是没有进行回滚
1)类内部方法调用奔雷的其他方法,是不会引起事务的,因为不能被AOP捕获,即使被调用方法使用@Transactional注解进行修饰。

2)抛出异常,但是被catch住了

修改方法:
1)在catch语句中,继续抛出Exception。不建议这么做,很不友好
2)对于要进行事务的方法,不使用try catch,在上层调用的时候处理异常;
3)在catch语句中增加TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();语句,手动回滚,这样上层就无需去处理异常。

Spring aop 异常捕获原理:被拦截的方法需显式抛出异常,不能自己进行try catch,这样aop代理才能捕获到方法的异常,才能进行回滚,默认情况下aop只捕获throw new RuntimeException的异常。 如果在事物方法中加入try catch,Spring 无法捕获异常,事物就无法回滚数据。数据正常存储,无法进行回滚。

public void insertRole(RoleBean roleBean) {
		deleteTerminalByRoleId(3);
	}
 
	@Transactional
	public void deleteRoleId(int roleId) {
		roleDao.deleteRoleId(roleId);
		throw new RuntimeException();
	}
如果 insertRole没有事务,调用有事务的deleteRoleId,即使deleteRoleId方法不捕获异常,那么Spring 事务也无法回滚数据,因为Spring 事务是AOP 动态代理,自己调用自己的方法,并不会产生代理对象,也就不会有AOP。这就是Spring 事务的自调用失效。(即insertRole方法加上@Transactional事务注解,那么数据会回滚,参考前文事务的传播)

@Transactional属性

属性 类型 描述
value String 可选的限定描述符,指定使用的事务管理器
propagation enum: Propagation 可选的事务传播行为设置
isolation enum: Isolation 可选的事务隔离级别设置
readOnly boolean 读写或只读事务,默认读写
timeout int (in seconds granularity) 事务超时时间设置
rollbackFor Class对象数组,必须继承自Throwable 导致事务回滚的异常类数组
rollbackForClassName 类名数组,必须继承自Throwable 导致事务回滚的异常类名字数组
noRollbackFor Class对象数组,必须继承自Throwable 不会导致事务回滚的异常类数组
noRollbackForClassName 类名数组,必须继承自Throwable

spring事务控制设置手动回滚

在aop配置事务控制或注解式控制事务中,try…catch…会使事务失效,可在catch中抛出运行时异常

throw new RuntimeException(e) 以便让aop捕获异常再去回滚

或者手动回滚TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();使得事务生效,异常回滚。

默认spring事务只在发生未被捕获的 runtimeExcetpion时才回滚。
spring aop 异常捕获原理:被拦截的方法需显式抛出异常,并不能经任何处理,这样aop代理才能捕获到方法的异常,才能进行回滚,默认情况下aop只捕获runtimeException的异常,但可以通过

配置来捕获特定的异常并回滚
换句话说在service的方法中不使用try catch 或者在catch中最后加上throw new runtimeExcetpion(),这样程序异常时才能被aop捕获进而回滚
解决方案:
方案1.例如service层处理事务,那么service中的方法中不做异常捕获,或者在catch语句中最后增加throw new RuntimeException()语句,以便让aop捕获异常再去回滚,并且在service上层(webservice客户端,view层action)要继续捕获这个异常并处理
方案2.在service层方法的catch语句中增加:TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();语句,手动回滚,这样上层就无需去处理异常(现在项目的做法)

当我们需要在事务控制的service层类中使用try catch 去捕获异常后,就会使事务控制失效,因为该类的异常并没有抛出,就不是触发事务管理机制。怎样才能即使用try catch去捕获异常,而又让出现异常后spring回滚呢,这里就要用到

TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();

完美解决问题。并且能够使该方法执行完。

使用Transaction注解时抛出异常但是事务不起作用,异常时事务没有进行回滚?

答:经过排查,查询在开启事务的方法中最外层使用了try...catch进行了异常的捕获,因此抛出的异常本捕获了,切面无法捕获到异常,所以不会进行回滚。

解决:(1) 手动指定切面捕获的异常类型(因为默认情况下只会在RuntimeExceptionimeException情况下才会进行事务的回滚),方式:@Transaction(rollbcackFor=Exception.class)

       (2) 在catch中手动抛出一个运行时异常即:throw new RuntimeException();

       (3) 如果需要在事务回滚时,给调用当前方法的调用者返回错误信息的话,用第二种方案就是不行的,因为抛出异常后的语句时不会执行的,包括return后面的语句,所以,此时可以手动进行事务回滚的语句调用即:TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();

你可能感兴趣的:(Spring事务)