关于java:Spring事务总结

3次阅读

共计 15825 个字符,预计需要花费 40 分钟才能阅读完成。

1. 什么是事务?

事务是逻辑上的一组操作,要么都执行,要么都不执行。

咱们零碎的每个业务办法可能包含了多个原子性的数据库操作,比方上面的 savePerson() 办法中就有两个原子性的数据库操作。这些原子性的数据库操作是有依赖的,它们要么都执行,要不就都不执行。

    public void savePerson() {personDao.save(person);
        personDetailDao.save(personDetail);
    }

另外,须要分外留神的是:事务是否失效数据库引擎是否反对事务是要害。比方罕用的 MySQL 数据库默认应用反对事务的 innodb 引擎。然而,如果把数据库引擎变为 myisam,那么程序也就不再反对事务了!

事务最经典也常常被拿出来说例子就是转账了。如果小明要给小红转账 1000 元,这个转账会波及到两个要害操作就是:

  1. 将小明的余额缩小 1000 元
  2. 将小红的余额减少 1000 元。

万一在这两个操作之间忽然呈现谬误比方银行零碎解体或者网络故障,导致小明余额缩小而小红的余额没有减少,这样就不对了。事务就是保障这两个要害操作要么都胜利,要么都要失败。

public class OrdersService {
    private AccountDao accountDao;

    public void setOrdersDao(AccountDao accountDao) {this.accountDao = accountDao;}

  @Transactional(propagation = Propagation.REQUIRED,
                isolation = Isolation.DEFAULT, readOnly = false, timeout = -1)
    public void accountMoney() {
    // 小红账户多 1000
        accountDao.addMoney(1000,xiaohong);
        // 模仿忽然呈现的异样,比方银行中可能为忽然停电等等
    // 如果没有配置事务管理的话会造成,小红账户多了 1000 而小明账户没有少钱
        int i = 10 / 0;
        // 小王账户少 1000
        accountDao.reduceMoney(1000,xiaoming);
    }
}

另外,数据库事务的 ACID 四大个性是事务的根底,上面简略来理解一下。

2. 事物的个性(ACID)理解么?

  • 原子性(Atomicity): 一个事务(transaction)中的所有操作,或者全副实现,或者全副不实现,不会完结在两头某个环节。事务在执行过程中产生谬误,会被回滚(Rollback)到事务开始前的状态,就像这个事务素来没有执行过一样。即,事务不可分割、不可约简。
  • 一致性(Consistency): 在事务开始之前和事务完结当前,数据库的完整性没有被毁坏。这示意写入的材料必须完全符合所有的预设束缚、触发器、级联回滚等。
  • 隔离性(Isolation): 数据库容许多个并发事务同时对其数据进行读写和批改的能力,隔离性能够避免多个事务并发执行时因为穿插执行而导致数据的不统一。事务隔离分为不同级别,包含未提交读(Read uncommitted)、提交读(read committed)、可反复读(repeatable read)和串行化(Serializable)。
  • 持久性(Durability): 事务处理完结后,对数据的批改就是永恒的,即使系统故障也不会失落。

参考)

3. 详谈 Spring 对事务的反对

再揭示一次:你的程序是否反对事务首先取决于数据库,比方应用 MySQL 的话,如果你抉择的是 innodb 引擎,那么祝贺你,是能够反对事务的。然而,如果你的 MySQL 数据库应用的是 myisam 引擎的话,那不好意思,从根上就是不反对事务的。

这里再多提一下一个十分重要的知识点:MySQL 怎么保障原子性的?

咱们晓得如果想要保障事务的原子性,就须要在异样产生时,对曾经执行的操作进行 回滚 ,在 MySQL 中,复原机制是通过  回滚日志(undo log) 实现的,所有事务进行的批改都会先先记录到这个回滚日志中,而后再执行相干的操作。如果执行过程中遇到异样的话,咱们间接利用 回滚日志 中的信息将数据回滚到批改之前的样子即可!并且,回滚日志会先于数据长久化到磁盘上。这样就保障了即便遇到数据库忽然宕机等状况,当用户再次启动数据库的时候,数据库还可能通过查问回滚日志来回滚将之前未实现的事务。

3.1. Spring 反对两种形式的事务管理

1). 编程式事务管理

通过 TransactionTemplate或者 TransactionManager 手动治理事务,理论利用中很少应用,然而对于你了解 Spring 事务管理原理有帮忙。

应用TransactionTemplate 进行编程式事务管理的示例代码如下:

@Autowired
private TransactionTemplate transactionTemplate;
public void testTransaction() {transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {

                try {// ....  业务代码} catch (Exception e){
                    // 回滚
                    transactionStatus.setRollbackOnly();}

            }
        });
}

应用 TransactionManager 进行编程式事务管理的示例代码如下:

@Autowired
private PlatformTransactionManager transactionManager;

public void testTransaction() {TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
          try {
               // ....  业务代码
              transactionManager.commit(status);
          } catch (Exception e) {transactionManager.rollback(status);
          }
}

2)申明式事务管理

举荐应用(代码侵入性最小),理论是通过 AOP 实现(基于@Transactional 的全注解形式应用最多)。

应用 @Transactional注解进行事务管理的示例代码如下:

@Transactional(propagation=propagation.PROPAGATION_REQUIRED)
public void aMethod {
  //do something
  B b = new B();
  C c = new C();
  b.bMethod();
  c.cMethod();}

3.2\. Spring 事务管理接口介绍

Spring 框架中,事务管理相干最重要的 3 个接口如下:

  • PlatformTransactionManager:(平台)事务管理器,Spring 事务策略的外围。
  • TransactionDefinition:事务定义信息(事务隔离级别、流传行为、超时、只读、回滚规定)。
  • TransactionStatus:事务运行状态。

咱们能够把 PlatformTransactionManager 接口能够被看作是事务下层的管理者,而 TransactionDefinition 和 TransactionStatus 这两个接口能够看作是事物的形容。

PlatformTransactionManager 会依据 TransactionDefinition 的定义比方事务超时工夫、隔离级别、流传行为等来进行事务管理,而 TransactionStatus 接口则提供了一些办法来获取事务相应的状态比方是否新事务、是否能够回滚等等。

3.2.1. PlatformTransactionManager: 事务管理接口

Spring 并不间接治理事务,而是提供了多种事务管理器。Spring 事务管理器的接口是:PlatformTransactionManager

通过这个接口,Spring 为各个平台如 JDBC(DataSourceTransactionManager)、Hibernate(HibernateTransactionManager)、JPA(JpaTransactionManager)等都提供了对应的事务管理器,然而具体的实现就是各个平台本人的事件了。

PlatformTransactionManager 接口的具体实现如下:

PlatformTransactionManager接口中定义了三个办法:

package org.springframework.transaction;

import org.springframework.lang.Nullable;

public interface PlatformTransactionManager {
    // 取得事务
    TransactionStatus getTransaction(@Nullable TransactionDefinition var1) throws TransactionException;
    // 提交事务
    void commit(TransactionStatus var1) throws TransactionException;
    // 回滚事务
    void rollback(TransactionStatus var1) throws TransactionException;
}

这里多插一嘴。为什么要定义或者说形象进去 PlatformTransactionManager 这个接口呢?

次要是因为要将事务管理行为形象进去,而后不同的平台去实现它,这样咱们能够保障提供给内部的行为不变,不便咱们扩大。我前段时间分享过:“为什么咱们要用接口?”

3.2.2. TransactionDefinition: 事务属性

事务管理器接口 PlatformTransactionManager 通过 getTransaction(TransactionDefinition definition) 办法来失去一个事务,这个办法外面的参数是 TransactionDefinition 类,这个类就定义了一些根本的事务属性。

那么什么是 事务属性 呢?

事务属性能够了解成事务的一些根本配置,形容了事务策略如何利用到办法上。

事务属性蕴含了 5 个方面:

TransactionDefinition 接口中定义了 5 个办法以及一些示意事务属性的常量比方隔离级别、流传行为等等。

package org.springframework.transaction;

import org.springframework.lang.Nullable;

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;
    // 返回事务的流传行为,默认值为 REQUIRED。int getPropagationBehavior();
    // 返回事务的隔离级别,默认值是 DEFAULT
    int getIsolationLevel();
    // 返回事务的超时工夫,默认值为 -1。如果超过该工夫限度但事务还没有实现,则主动回滚事务。int getTimeout();
    // 返回是否为只读事务,默认值为 false
    boolean isReadOnly();

    @Nullable
    String getName();}

3.2.3. TransactionStatus: 事务状态

TransactionStatus接口用来记录事务的状态 该接口定义了一组办法, 用来获取或判断事务的相应状态信息。

PlatformTransactionManager.getTransaction(…)办法返回一个 TransactionStatus 对象。

TransactionStatus 接口接口内容如下:

public interface TransactionStatus{boolean isNewTransaction(); // 是否是新的事物
    boolean hasSavepoint(); // 是否有复原点
    void setRollbackOnly();  // 设置为只回滚
    boolean isRollbackOnly(); // 是否为只回滚
    boolean isCompleted; // 是否已实现
}

3.3. 事务属性详解

理论业务开发中,大家个别都是应用 @Transactional 注解来开启事务,很多人并不分明这个参数外面的参数是什么意思,有什么用。为了更好的在我的项目中应用事务管理,强烈推荐好好浏览一下上面的内容。

3.3.1. 事务流传行为

事务流传行为是为了解决业务层办法之间相互调用的事务问题

当事务办法被另一个事务办法调用时,必须指定事务应该如何流传。例如:办法可能持续在现有事务中运行,也可能开启一个新事务,并在本人的事务中运行。

举个例子!

咱们在 A 类的 aMethod() 办法中调用了 B 类的 bMethod() 办法。这个时候就波及到业务层办法之间相互调用的事务问题。如果咱们的 bMethod()如果产生异样须要回滚,如何配置事务流传行为能力让 aMethod()也跟着回滚呢?这个时候就须要事务流传行为的常识了,如果你不晓得的话肯定要好好看一下。

Class A {@Transactional(propagation=propagation.xxx)
    public void aMethod {
        //do something
        B b = new B();
        b.bMethod();}
}

Class B {@Transactional(propagation=propagation.xxx)
    public void bMethod {//do something}
}

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;
    ......
}

不过如此,为了方便使用,Spring 会相应地定义了一个枚举类:Propagation

package org.springframework.transaction.annotation;

import org.springframework.transaction.TransactionDefinition;

public enum Propagation {REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED),

    SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS),

    MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY),

    REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW),

    NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED),

    NEVER(TransactionDefinition.PROPAGATION_NEVER),

    NESTED(TransactionDefinition.PROPAGATION_NESTED);

    private final int value;

    Propagation(int value) {this.value = value;}

    public int value() {return this.value;}

}

正确的事务流传行为可能的值如下

1.TransactionDefinition.PROPAGATION_REQUIRED

应用的最多的一个事务流传行为,咱们平时常常应用的 @Transactional 注解默认应用就是这个事务流传行为。如果以后存在事务,则退出该事务;如果以后没有事务,则创立一个新的事务。也就是说:

  1. 如果内部办法没有开启事务的话,Propagation.REQUIRED润饰的外部办法会新开启本人的事务,且开启的事务互相独立,互不烦扰。
  2. 如果内部办法开启事务并且被 Propagation.REQUIRED 的话,所有 Propagation.REQUIRED 润饰的外部办法和内部办法均属于同一事务,只有一个办法回滚,整个事务均回滚。

举个例子:如果咱们下面的 aMethod()bMethod()应用的都是 PROPAGATION_REQUIRED 流传行为的话,两者应用的就是同一个事务,只有其中一个办法回滚,整个事务均回滚。

Class A {@Transactional(propagation=propagation.PROPAGATION_REQUIRED)
    public void aMethod {
        //do something
        B b = new B();
        b.bMethod();}
}

Class B {@Transactional(propagation=propagation.PROPAGATION_REQUIRED)
    public void bMethod {//do something}
}

2.TransactionDefinition.PROPAGATION_REQUIRES_NEW

创立一个新的事务,如果以后存在事务,则把以后事务挂起。也就是说不论内部办法是否开启事务,Propagation.REQUIRES_NEW润饰的外部办法会新开启本人的事务,且开启的事务互相独立,互不烦扰。

举个例子:如果咱们下面的 bMethod() 应用 PROPAGATION_REQUIRES_NEW 事务流传行为润饰,aMethod还是用 PROPAGATION_REQUIRED 润饰的话。如果 aMethod() 产生异样回滚,bMethod()不会跟着回滚,因为 bMethod()开启了独立的事务。然而,如果 bMethod()抛出了未被捕捉的异样并且这个异样满足事务回滚规定的话,aMethod()同样也会回滚,因为这个异样被 aMethod()的事务管理机制检测到了。

Class A {@Transactional(propagation=propagation.PROPAGATION_REQUIRED)
    public void aMethod {
        //do something
        B b = new B();
        b.bMethod();}
}

Class B {@Transactional(propagation=propagation.REQUIRES_NEW)
    public void bMethod {//do something}
}

3.TransactionDefinition.PROPAGATION_NESTED:

如果以后存在事务,则创立一个事务作为以后事务的嵌套事务来运行;如果以后没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。也就是说:

  1. 在内部办法未开启事务的状况下 Propagation.NESTEDPropagation.REQUIRED作用雷同,润饰的外部办法都会新开启本人的事务,且开启的事务互相独立,互不烦扰。
  2. 如果内部办法开启事务的话,Propagation.NESTED润饰的外部办法属于内部事务的子事务,内部主事务回滚的话,子事务也会回滚,而外部子事务能够独自回滚而不影响内部主事务和其余子事务。

这里还是简略举个例子:

如果 aMethod() 回滚的话,bMethod()bMethod2() 都要回滚,而 bMethod() 回滚的话,并不会造成 aMethod() 和 bMethod() 回滚。

Class A {@Transactional(propagation=propagation.PROPAGATION_REQUIRED)
    public void aMethod {
        //do something
        B b = new B();
        b.bMethod();
        b.bMethod2();}
}

Class B {@Transactional(propagation=propagation.PROPAGATION_NESTED)
    public void bMethod {//do something}
    @Transactional(propagation=propagation.PROPAGATION_NESTED)
    public void bMethod2 {//do something}
}

4.TransactionDefinition.PROPAGATION_MANDATORY

如果以后存在事务,则退出该事务;如果以后没有事务,则抛出异样。(mandatory:强制性)

这个应用的很少,就不举例子来说了。

若是谬误的配置以下 3 种事务流传行为,事务将不会产生回滚,这里不对照案例解说了,应用的很少。

  • TransactionDefinition.PROPAGATION_SUPPORTS: 如果以后存在事务,则退出该事务;如果以后没有事务,则以非事务的形式持续运行。
  • TransactionDefinition.PROPAGATION_NOT_SUPPORTED: 以非事务形式运行,如果以后存在事务,则把以后事务挂起。
  • TransactionDefinition.PROPAGATION_NEVER: 以非事务形式运行,如果以后存在事务,则抛出异样。

更多对于事务流传行为的内容请看这篇文章:《太难了~ 面试官让我联合案例讲讲本人对 Spring 事务流传行为的了解。》

3.3.2 事务隔离级别

TransactionDefinition 接口中定义了五个示意隔离级别的常量:

public interface TransactionDefinition {
    ......
    int ISOLATION_DEFAULT = -1;
    int ISOLATION_READ_UNCOMMITTED = 1;
    int ISOLATION_READ_COMMITTED = 2;
    int ISOLATION_REPEATABLE_READ = 4;
    int ISOLATION_SERIALIZABLE = 8;
    ......
}

和事务流传行为这块一样,为了方便使用,Spring 也相应地定义了一个枚举类:Isolation

public enum Isolation {DEFAULT(TransactionDefinition.ISOLATION_DEFAULT),

    READ_UNCOMMITTED(TransactionDefinition.ISOLATION_READ_UNCOMMITTED),

    READ_COMMITTED(TransactionDefinition.ISOLATION_READ_COMMITTED),

    REPEATABLE_READ(TransactionDefinition.ISOLATION_REPEATABLE_READ),

    SERIALIZABLE(TransactionDefinition.ISOLATION_SERIALIZABLE);

    private final int value;

    Isolation(int value) {this.value = value;}

    public int value() {return this.value;}

}

上面我顺次对每一种事务隔离级别进行介绍:

  • TransactionDefinition.ISOLATION_DEFAULT : 应用后端数据库默认的隔离级别,MySQL 默认采纳的 REPEATABLE_READ 隔离级别 Oracle 默认采纳的 READ_COMMITTED 隔离级别.
  • TransactionDefinition.ISOLATION_READ_UNCOMMITTED : 最低的隔离级别,应用这个隔离级别很少,因为它容许读取尚未提交的数据变更,可能会导致脏读、幻读或不可反复读
  • TransactionDefinition.ISOLATION_READ_COMMITTED : 容许读取并发事务曾经提交的数据,能够阻止脏读,然而幻读或不可反复读仍有可能产生
  • TransactionDefinition.ISOLATION_REPEATABLE_READ : 对同一字段的屡次读取后果都是统一的,除非数据是被自身事务本人所批改,能够阻止脏读和不可反复读,但幻读仍有可能产生。
  • TransactionDefinition.ISOLATION_SERIALIZABLE : 最高的隔离级别,齐全遵从 ACID 的隔离级别。所有的事务顺次一一执行,这样事务之间就齐全不可能产生烦扰,也就是说,该级别能够避免脏读、不可反复读以及幻读。然而这将重大影响程序的性能。通常状况下也不会用到该级别。

因为平时应用 MySQL 数据库比拟多,这里再多提一嘴!

MySQL InnoDB 存储引擎的默认反对的隔离级别是 REPEATABLE-READ(可重读)。咱们能够通过 SELECT @@tx_isolation; 命令来查看,MySQL 8.0 该命令改为SELECT @@transaction_isolation;

mysql> SELECT @@tx_isolation;
+-----------------+
| @@tx_isolation  |
+-----------------+
| REPEATABLE-READ |
+-----------------+

这里须要留神的是:与 SQL 规范不同的中央在于 InnoDB 存储引擎在 REPEATABLE-READ(可重读) 事务隔离级别下应用的是 Next-Key Lock 锁算法,因而能够防止幻读的产生,这与其余数据库系统 (如 SQL Server) 是不同的。所以说 InnoDB 存储引擎的默认反对的隔离级别是 REPEATABLE-READ(可重读) 曾经能够齐全保障事务的隔离性要求,即达到了 SQL 规范的 SERIALIZABLE(可串行化) 隔离级别。

因为隔离级别越低,事务申请的锁越少,所以大部分数据库系统的隔离级别都是 READ-COMMITTED(读取提交内容) :,然而你要晓得的是 InnoDB 存储引擎默认应用 REPEATABLE-READ(可重读) 并不会什么任何性能上的损失。

更多对于事务隔离级别的内容请看:

  1. 《一文带你轻松搞懂事务隔离级别(图文详解)》
  2. 面试官:你说对 MySQL 事务很熟?那我问你 10 个问题

3.3.3. 事务超时属性

所谓事务超时,就是指一个事务所容许执行的最长工夫,如果超过该工夫限度但事务还没有实现,则主动回滚事务。在 TransactionDefinition 中以 int 的值来示意超时工夫,其单位是秒,默认值为 -1。

3.3.3. 事务只读属性

package org.springframework.transaction;

import org.springframework.lang.Nullable;

public interface TransactionDefinition {
    ......
    // 返回是否为只读事务,默认值为 false
    boolean isReadOnly();}

对于只有读取数据查问的事务,能够指定事务类型为 readonly,即只读事务。只读事务不波及数据的批改,数据库会提供一些优化伎俩,适宜用在有多条数据库查问操作的办法中。

很多人就会疑难了,为什么我一个数据查问操作还要启用事务反对呢?

拿 MySQL 的 innodb 举例子,依据官网 https://dev.mysql.com/doc/refman/5.7/en/innodb-autocommit-commit-rollback.html 形容:

MySQL 默认对每一个新建设的连贯都启用了 autocommit 模式。在该模式下,每一个发送到 MySQL 服务器的 sql 语句都会在一个独自的事务中进行解决,执行完结后会主动提交事务,并开启一个新的事务。

然而,如果你给办法加上了 Transactional 注解的话,这个办法执行的所有 sql 会被放在一个事务中。如果申明了只读事务的话,数据库就会去优化它的执行,并不会带来其余的什么收益。

如果不加 Transactional,每条sql 会开启一个独自的事务,两头被其它事务改了数据,都会实时读取到最新值。

分享一下对于事务只读属性,其他人的解答:

  1. 如果你一次执行单条查问语句,则没有必要启用事务反对,数据库默认反对 SQL 执行期间的读一致性;
  2. 如果你一次执行多条查问语句,例如统计查问,报表查问,在这种场景下,多条查问 SQL 必须保障整体的读一致性,否则,在前条 SQL 查问之后,后条 SQL 查问之前,数据被其余用户扭转,则该次整体的统计查问将会呈现读数据不统一的状态,此时,应该启用事务反对

3.3.4. 事务回滚规定

这些规定定义了哪些异样会导致事务回滚而哪些不会。默认状况下,事务只有遇到运行期异样(RuntimeException 的子类)时才会回滚,Error 也会导致事务回滚,然而,在遇到查看型(Checked)异样时不会回滚。

如果你想要回滚你定义的特定的异样类型的话,能够这样:

@Transactional(rollbackFor= MyException.class)

3.4. @Transactional 注解应用详解

1) @Transactional 的作用范畴

  1. 办法 :举荐将注解应用于办法上,不过须要留神的是: 该注解只能利用到 public 办法上,否则不失效。
  2. :如果这个注解应用在类上的话,表明该注解对该类中所有的 public 办法都失效。
  3. 接口:不举荐在接口上应用。

2) @Transactional 的罕用配置参数

@Transactional注解源码如下,外面蕴含了根本事务属性的配置:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {@AliasFor("transactionManager")
    String value() default "";

    @AliasFor("value")
    String transactionManager() default "";

    Propagation propagation() default Propagation.REQUIRED;

    Isolation isolation() default Isolation.DEFAULT;

    int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;

    boolean readOnly() default false;

    Class<? extends Throwable>[] rollbackFor() default {};

    String[] rollbackForClassName() default {};

    Class<? extends Throwable>[] noRollbackFor() default {};

    String[] noRollbackForClassName() default {};}

@Transactional 的罕用配置参数总结(只列巨额 5 个我平时比拟罕用的):

属性名 阐明
propagation 事务的流传行为,默认值为 REQUIRED,可选的值在下面介绍过
isolation 事务的隔离级别,默认值采纳 DEFAULT,可选的值在下面介绍过
timeout 事务的超时工夫,默认值为 -1(不会超时)。如果超过该工夫限度但事务还没有实现,则主动回滚事务。
readOnly 指定事务是否为只读事务,默认值为 false。
rollbackFor 用于指定可能触发事务回滚的异样类型,并且能够指定多个异样类型。

3)@Transactional 事务注解原理

面试中在问 AOP 的时候可能会被问到的一个问题。简略说下吧!

咱们晓得,@Transactional 的工作机制是基于 AOP 实现的,AOP 又是应用动静代理实现的。如果指标对象实现了接口,默认状况下会采纳 JDK 的动静代理,如果指标对象没有实现了接口, 会应用 CGLIB 动静代理。

多提一嘴:createAopProxy() 办法 决定了是应用 JDK 还是 Cglib 来做动静代理,源码如下:

public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {

    @Override
    public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {Class<?> targetClass = config.getTargetClass();
            if (targetClass == null) {
                throw new AopConfigException("TargetSource cannot determine target class:" +
                        "Either an interface or a target is required for proxy creation.");
            }
            if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {return new JdkDynamicAopProxy(config);
            }
            return new ObjenesisCglibAopProxy(config);
        }
        else {return new JdkDynamicAopProxy(config);
        }
    }
  .......
}

如果一个类或者一个类中的 public 办法上被标注 @Transactional 注解的话,Spring 容器就会在启动的时候为其创立一个代理类,在调用被@Transactional 注解的 public 办法的时候,理论调用的是,TransactionInterceptor 类中的 invoke() 办法。这个办法的作用就是在指标办法之前开启事务,办法执行过程中如果遇到异样的时候回滚事务,办法调用实现之后提交事务。

TransactionInterceptor 类中的 invoke()办法外部理论调用的是 TransactionAspectSupport 类的 invokeWithinTransaction()办法。因为新版本的 Spring 对这部分重写很大,而且用到了很多响应式编程的常识,这里就不列源码了。

4)Spring AOP 自调用问题

若同一类中的其余没有 @Transactional 注解的办法外部调用有 @Transactional 注解的办法,有@Transactional 注解的办法的事务会生效。

这是因为 Spring AOP 代理的起因造成的,因为只有当 @Transactional 注解的办法在类以外被调用的时候,Spring 事务管理才失效。

MyService 类中的 method1() 调用 method2() 就会导致 method2() 的事务生效。

@Service
public class MyService {private void method1() {method2();
     //......
}
@Transactional
 public void method2() {//......}
}

解决办法就是防止同一类中自调用或者应用 AspectJ 取代 Spring AOP 代理。

5) @Transactional 的应用注意事项总结

  1. @Transactional 注解只有作用到 public 办法上事务才失效,不举荐在接口上应用;
  2. 防止同一个类中调用 @Transactional 注解的办法,这样会导致事务生效;
  3. 正确的设置 @Transactional 的 rollbackFor 和 propagation 属性,否则事务可能会回滚失败
  4. ……

4. Reference

  1. [总结]Spring 事务管理中 @Transactional 的参数:http://www.mobabel.net/spring 事务管理中 transactional 的参数 /
  2. Spring 官网文档:https://docs.spring.io/spring/docs/4.2.x/spring-framework-reference/html/transaction.html
  3. 《Spring5 高级编程》
  4. 透彻的把握 Spring 中 @transactional 的应用: https://www.ibm.com/developerworks/cn/java/j-master-spring-transactional-use/index.html
  5. Spring 事务的流传个性:https://github.com/love-somnus/Spring/wiki/Spring 事务的流传个性
  6. Spring 事务流传行为详解:https://segmentfault.com/a/1190000013341344
  7. 全面剖析 Spring 的编程式事务管理及申明式事务管理:https://www.ibm.com/developerworks/cn/education/opensource/os-cn-spring-trans/index.html
正文完
 0