Spring @Transactional 事宜机制

Spring @Transactional 事务机制

Spring @Transactional 事务机制

 

几个概念要清楚:事务的传播机制,事务的边界

 

工作原理

运行配置@Transactional注解的测试类的时候,具体会发生如下步骤

1)事务开始时,通过AOP机制,生成一个代理connection对象,并将其放入DataSource实例的某个与DataSourceTransactionManager相关的某处容器中。在接下来的整个事务中,客户代码都应该使用该connection连接数据库,执行所有数据库命令[不使用该connection连接数据库执行的数据库命令,在本事务回滚的时候得不到回滚]

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

 

 

那么@Transactional如何工作?

 

实现了EntityManager接口的持久化上下文代理并不是声明式事务管理的唯一部分,事实上包含三个组成部分:

1. EntityManager Proxy本身

2. 事务的切面

3. 事务管理器

 

事务的切面

事务的切面是一个“around(环绕)”切面,在注解的业务方法前后都可以被调用。实现切面的具体类是TransactionInterceptor。

 

事务的切面有两个主要职责:

1. 在’before’时,切面提供一个调用点,来决定被调用业务方法应该在正在进行事务的范围内运行,还是开始一个新的独立事务。

2. 在’after’时,切面需要确定事务被提交,回滚或者继续运行。

3. 在’before’时,事务切面自身不包含任何决策逻辑,是否开始新事务的决策委派给事务管理器完成。

 

 

事务管理器

事务管理器需要解决下面两个问题:

新的Entity Manager是否应该被创建?

是否应该开始新的事务?

 

这些需要事务切面’before’逻辑被调用时决定。事务管理器的决策基于以下两点:

1. 事务是否正在进行

2. 事务方法的propagation属性(比如REQUIRES_NEW总要开始新事务)

 

如果事务管理器确定要创建新事务,那么将:

1. 创建一个新的entity manager

2. entity manager绑定到当前线程

3. 从数据库连接池中获取连接

4. 将连接绑定到当前线程

 

特点:

1. 使用ThreadLocal变量将entity manager和数据库连接都绑定到当前线程。

2. 事务运行时他们存储在线程中,当它们不再被使用时,事务管理器决定是否将他们清除。

3. 程序的任何部分如果需要当前的entity manager和数据库连接都可以从线程中获取。

 

 

EntityManager proxy

 

EntityManager proxy(前面已经介绍过)就是谜题的最后一部分。当业务方法调用entityManager.persist()时,这不是由entity manager直接调用的。

而是业务方法调用代理,代理从线程获取当前的entity manager,前面介绍过事务管理器将entity manager绑定到线程。

了解了@Transactional机制的各个部分,我们来看一下实现它的常用Spring配置。

 

 

 

根据上面所述,我们所使用的客户代码应该具有如下能力:

1)每次执行数据库命令的时候

如果在事务的上下文环境中,那么不直接创建新的connection对象,而是尝试从DataSource实例的某个与DataSourceTransactionManager相关的某处容器中获取connection对象;在非事务的上下文环境中,直接创建新的connection对象

2)每次执行完数据库命令的时候

如果在事务的上下文环境中,那么不直接关闭connection对象,因为在整个事务中都需要使用该connection对象,而只是释放本次数据库命令对该connection对象的持有;在非事务的上下文环境中,直接关闭该connection对象

 

 

在service类前加上@Transactional,声明这个service所有方法需要事务管理。每一个业务方法开始时都会打开一个事务。

Spring默认情况下会对运行期例外(RunTimeException)进行事务回滚。这个例外是unchecked

如果遇到checked意外就不回滚。

如何改变默认规则:

1 让checked例外也回滚:在整个方法前加上 @Transactional(rollbackFor=Exception.class)

2 让unchecked例外不回滚: @Transactional(notRollbackFor=RunTimeException.class)

3 不需要事务管理的(只查询的)方法:@Transactional(propagation=Propagation.NOT_SUPPORTED)

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

 

注意:

如果异常被try{}catch{}了,事务就不回滚了,如果想让事务回滚必须再往外抛try{}catch{throw Exception}。

 

 

参考:

http://www.importnew.com/12300.html