xiaobaoqiu Blog

Think More, Code Less

从代理到Spring事务3-Spring事务

1.Spring事务

我们使用事务的目标:

1
The most common reason for using transactions in an application is to maintain a high degree of data integrity and consistency.

我的理解Spring事务中包括的几个主要要素,下面会一一讲解:

1
2
3
1.事务定义:事务包含哪些属性
2.事务管理器:如何将我们对事务的诉求转化为数据库层面的实现
3.事务切面:Spring底层是如何将业务和事务串起来的

关于事务的使用是最简单的,通常分为 编程式事务 和 注解式事务:

编程式事务

代码直接使用PlatformTransactionManager,因此需要使用者自己管理事务的创建,提交,回滚,挂起等逻辑。

优点:灵活度大; 缺点:使用难度大,易出错,业务代码侵入大;

一个编程式事务的完整Demo

我们可以使用 Spring 提供的 TransactionTemplate 类来简化编程事务:

1
2
3
4
5
6
7
8
9
10
TransactionTemplate template = new TransactionTemplate();
template.setIsolationLevel(org.springframework.transaction.TransactionDefinition.ISOLATION_DEFAULT);
template.setPropagationBehavior(org.springframework.transaction.TransactionDefinition.PROPAGATION_REQUIRED);
template.setTransactionManager(transactionManager);
template.execute(new TransactionCallback<UserAo>() {
    @Override
    public UserAo doInTransaction(TransactionStatus status) {
         //business logic
    }
});

我们可以看一下 TransactionTemplate 的实现代码,TransactionTemplate继承了DefaultTransactionDefinition(实现了TransactionDefinition),使用TransactionDefinition需要我们给定一个TransactionManager,可以修改 事务隔离级别,事务传播行为等事务属性(其实是DefaultTransactionDefinition提供的功能)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public <T> T execute(TransactionCallback<T> action) throws TransactionException {
    TransactionStatus status = this.transactionManager.getTransaction(this);    //获取并初始化事务,处理比如事务传播形式等
    T result;
    try {
        result = action.doInTransaction(status);    //业务逻辑,包括自定义的回滚
    } catch (RuntimeException ex) {
        // Transactional code threw application exception -> rollback
        rollbackOnException(status, ex);
        throw ex;
    } catch (Error err) {
        // Transactional code threw error -> rollback
        rollbackOnException(status, err);
        throw err;
    } catch (Exception ex) {
        // Transactional code threw unexpected exception -> rollback
        rollbackOnException(status, ex);
        throw new UndeclaredThrowableException(ex, "TransactionCallback threw undeclared checked exception");
    }
    this.transactionManager.commit(status); //事务提交
    return result;
}

其中 transactionManager.getTransaction(this) 的代码后面会详细分析。

注解式事务

使用 @Transactional 来管理事务,我们需要声明一个全局的事务管理器,以及事务对注解的支持:

1
2
3
4
5
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>

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

优点:使用简单,业务代码侵入小; 缺点:理解有难度;

注意:Spring建议在具体业务实现类上使用Transactional注解,而不是在接口或则接口的方法上配置@Transactional注解:

1
2
3
4
The Spring team’s recommendation is that you only annotate concrete classes with the @Transactional annotation, as opposed to annotating interfaces. 
You certainly can place the @Transactional annotation on an interface (or an interface method), but this will only work as you would expect it to if you are using interface-based proxies. 
The fact that annotations are not inherited means that if you are using class-based proxies (proxy-target-class=“true“) or the weaving-based aspect (mode=“aspectj“) then the transaction settings will not be recognised by the proxying/weaving infrastructure and the object will not be wrapped in a transactional proxy (which would be decidedly bad). 
So please do take the Spring team’s advice and only annotate concrete classes (and the methods of concrete classes) with the @Transactional annotation.

Transactional注解上可以设置很多事务相关的参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
public @interface Transactional {
    /**
     * 用于配置事务管理器,当系统中存在多个数据源多个事务管理器的时候有用
     * 配置 txManager1 这种 bean name
     */
    String value() default "";

    /**
     * 事务传播级别
     */
    Propagation propagation() default Propagation.REQUIRED;

    /**
     * 事务隔离级别
     */
    Isolation isolation() default Isolation.DEFAULT;

    /**
     * 事务超时时间
     */
    int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;

    /**
     * 事务是否read-only
     * 注意:这个设置对真实的事务执行系统是一个 hint 而不是 强制要求有这个配置的情况下 write 一定导致失败
     */
    boolean readOnly() default false;

    /**
     * 定义0个或多个exception,当事务遇到这些异常的时候,事务会回滚
     */
    Class<? extends Throwable>[] rollbackFor() default {};

    /**
     * 定义0个或多个exception的name,当事务遇到这些异常的时候,事务会回滚
     * 最好定义完整的路径,比如 javax.servlet.ServletException 会代表 ServletException 本身及子类
     */
    String[] rollbackForClassName() default {};

    /**
     * 定义0个或多个exception,当事务遇到这些异常的时候,事务不会回滚
     */
    Class<? extends Throwable>[] noRollbackFor() default {};

    /**
     * 定义0个或多个exception的name,当事务遇到这些异常的时候,事务不会回滚
     */
    String[] noRollbackForClassName() default {};
}

1.1 事务定义

如何定义一个事务,Spring中使用 TransactionDefinition 来定义一个事务的属性,使用接口的形式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public interface TransactionDefinition {
    /**
     * 事务传播行为,可选项参见 TransactionDefinition 中定义的 PROPAGATION_XXX
     */
    int getPropagationBehavior();

    /**
     * 事务隔离级别,可选项参见 TransactionDefinition 中定义的 ISOLATION_XXX
     * 只在 PROPAGATION_REQUIRED 和 PROPAGATION_REQUIRES_NEW 这两个事务传播行为配置下有效
     */
    int getIsolationLevel();

    /**
     * 事务超时时间,单位秒
     * 默认 TIMEOUT_DEFAULT = -1 表示使用底层实现的超时时间或者没有(不支持事务超时时间)
     * 只在 PROPAGATION_REQUIRED 和 PROPAGATION_REQUIRES_NEW 这两个事务传播行为配置下有效
     * 如果事务管理器不支持设置超时时间,而调用了这个接口会抛出异常
     */
    int getTimeout();

    boolean isReadOnly();

    /**
     * 事务name,允许为null
     * 对于申明式事务,默认的name为:完整的class名称.metho名称
     */
    String getName();
}

TransactionDefinition 有一个默认实现 DefaultTransactionDefinition,其中定义了事务几个属性的默认值:

1
2
3
4
private int propagationBehavior = PROPAGATION_REQUIRED;
private int isolationLevel = ISOLATION_DEFAULT;
private int timeout = TIMEOUT_DEFAULT;
private boolean readOnly = false;
事务传播

关于事务的传播行为,这里简单介绍一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/**
 * 如果已经存在事务直接使用,如果不存在则创建一个新事务
 */
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;

其中 PROPAGATION_NESTED 理解有点困难,可以参考 Spring 的文档

1
2
3
PROPAGATION_NESTED uses a single physical transaction with multiple savepoints that it can roll back to. 
Such partial rollbacks allow an inner transaction scope to trigger a rollback for its scope, with the outer transaction being able to continue the physical transaction despite some operations having been rolled back. 
This setting is typically mapped onto JDBC savepoints, so will only work with JDBC resource transactions. See Spring's DataSourceTransactionManager.

大致意思就是关于 SavePoint,JDBC定义了java.sql.SavePoint接口,提供在一个更细粒度的事务控制机制。当设置了一个保存点后,可以rollback到该保存点处的状态,而不是rollback整个事务。Connection接口的setSavepoint和releaseSavepoint方法可以设置和释放保存点。 而Spring中定义了 SavepointManager,用于 SavePoint 的创建(createSavepoint),回滚(rollbackToSavepoint),释放(releaseSavepoint)。

事务隔离级别

关于事务的隔离级别,也简单介绍一下,除了 ISOLATION_DEFAULT ,其余均使用 java.sql.Connection 中的隔离级别的定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
 * 使用底层数据源的隔离级别
 * 参考:java.sql.Connection.getTransactionIsolation()
 */
int ISOLATION_DEFAULT = -1;

/**
 * READ_UNCOMMITTED
 */
int ISOLATION_READ_UNCOMMITTED = Connection.TRANSACTION_READ_UNCOMMITTED;

/**
 * READ_COMMITTED
 */
int ISOLATION_READ_COMMITTED = Connection.TRANSACTION_READ_COMMITTED;

/**
 * REPEATABLE_READ
 */
int ISOLATION_REPEATABLE_READ = Connection.TRANSACTION_REPEATABLE_READ;

/**
 * SERIALIZABLE
 */
int ISOLATION_SERIALIZABLE = Connection.TRANSACTION_SERIALIZABLE;

1.2 事务管理器

事务管理 Spring 使用 PlatformTransactionManager 定义,其中包含一个事务的三个主要操作,在业务开始之前开始事务,执行业务逻辑之后提交事务,业务逻辑异常则回滚事务:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public interface PlatformTransactionManager {
    /**
     * 根据事务定义(TransactionDefinition),返回当前active事务或者创建一个新的事务
     * 注意:
     * 1.TransactionDefinition 中的隔离级别超时时间等设置只在创建了新事物的时候有效,如果使用当前已存在的事务则无效
     * 2.不是所有的事务管理器都支持 TransactionDefinition 中的所有属性,当事务管理器遇到不支持的属性则抛异常
     * 3.上条规则的例外是 read-only这个属性,如果事务管理器不支持这个属性直接忽略
     */
    TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;

    /**
     * 事务提交
     */
    void commit(TransactionStatus status) throws TransactionException;

    /**
     * 回滚事务
     */
    void rollback(TransactionStatus status) throws TransactionException;
}

我们熟悉的 JDBC 支持的事务管理器 DataSourceTransactionManager 就是其一个实现,其他还包括 JtaTransactionManager 等。

同时,Spring提供了一个 TransactionStatus 的就看便于再代码中控制事务以及获取事物状态。在PlatformTransactionManager中获取事务的返回值就是一个 TransactionStatus 对象。

1.3 How Transactional Works

下面会讲解我们使用的 Transactional 注解的工作原理,即当我们在Service的某个public方法上加上 @Transactional之后,从编译到调用这个方法整个过程中会发生什么,事务再其中如何起作用。

这个好像没什么讲的,就是AOP的实现原理。简单的说就是创建Bean的时候会创建一个代理Bean,使用AOP实现事务,参考上一届的AOP实现原理。我们可以参考 TransactionInterceptor 的代码。

1.4 线程保持

1.5 Roll Back

主要关注Spring事务的回滚,其实主要就是要搞明白Spring在什么情况下会回滚。

1.6 Pitfalls

事务的坑 http://www.ibm.com/developerworks/library/j-ts1/

代理实现选择器: AdviceMode TransactionManagementConfigurationSelector

声明事务切面实现: TransactionInterceptor

事务实现 Connection关闭自动提交 ConnectionHolder来保持Connection

参考: http://openwares.net/java/spring_mybatis_transaction.html http://my.oschina.net/guanzhenxing/blog/214228 http://www.mybatis.org/spring/zh/transactions.html http://tracylihui.github.io/2015/07/28/spring/Spring%E5%A3%B0%E6%98%8E%E5%BC%8F%E4%BA%8B%E5%8A%A1%E7%AE%A1%E7%90%86/ http://www.cnblogs.com/davenkin/archive/2013/02/16/java-tranaction-1.html http://www.importnew.com/12300.html 详细介绍Spring事务管理: http://developer.51cto.com/art/200906/129854.htm spring事务原理1-事务的抽象: http://sqtds.github.io/2014/06/09/2014/spring-tx1/ Spring官方文档Transaction Management: http://docs.spring.io/autorepo/docs/spring/3.2.x/spring-framework-reference/html/transaction.html Spring官方文档Aspect Oriented Programming with Spring: http://docs.spring.io/spring/docs/3.0.x/reference/aop.html StackOverFlow Spring - @Transactional - What happens in background?: http://stackoverflow.com/questions/1099025/spring-transactional-what-happens-in-background Transaction strategies: Understanding transaction pitfalls: http://www.ibm.com/developerworks/library/j-ts1/ Annotation-based Transactions in Spring: http://springinpractice.com/2008/03/18/annotation-based-transactions-in-spring Design principle behind Transaction framework with Spring 3: http://stackoverflow.com/questions/11789857/design-principle-behind-transaction-framework-with-spring-3 Chapter 6. 使用Spring进行面向切面编程(AOP): http://shouce.jb51.net/spring/aop.html wiki: http://wiki.corp.qunar.com/display/~yushen.ma/Spring+3.x-JDBC+Transaction spring事务: http://wiki.corp.qunar.com/pages/viewpage.action?pageId=52084161 spring 事务: http://wiki.corp.qunar.com/pages/viewpage.action?pageId=93338632 aspectj事务: http://wiki.corp.qunar.com/pages/viewpage.action?pageId=92656954

Demo: 编程事务: http://www.tutorialspoint.com/spring/programmatic_management.htm 声明事务: http://www.tutorialspoint.com/spring/declarative_management.htm

@Transactional注解工作原理:http://blog.csdn.net/dslztx/article/details/46636079 分布式事务系列(1.2)Spring的事务体系: https://yq.aliyun.com/articles/39046 spring transaction源码分析–事务架构:http://www.cnblogs.com/davidwang456/p/4309038.html

http://blog.csdn.net/szwangdf/article/details/41516239

AopProxy DefaultAopProxyFactory