Spring事务隔离级别和传播级别测试分析
在某些情况下, 我们需要修改某事务事务隔离级别从Spring默认级别**(此处使用
MySql, 默认为ISOLATION_REPEATABLE_READ)**为脏读, 从而读取其他事务没有提交的数据. 于是对事务隔离级别和传播属性作了个简单测试.测试组合如下:
名称 ISOLATION PROPOGATION 结果(是否可读) NONE ~~~~ ~~~~ true BOTH_DEFAULT ISOLATION_REPEATABLE_READ**(DEFAULT)** REQUIRED**(DEFAULT)** true REQUIRED_NEW (DEFAULT) REQUIRES_NEW false READ_UNCOMMITTED READ_UNCOMMITTED REQUIRED**(DEFAULT)** true REQUIRES_NEW_READ_UNCOMMITTED READ_UNCOMMITTED REQUIRES_NEW true 表1
1. 测试步骤:
1.1 测试调用(原始事务)
@Transactional(rollbackFor = {Exception.class})
@Override
public String create(ApplicationDTO applicationDTO, BaseParam baseParam) {
final ApplicationPO applicationPO = createApplication(applicationDTO);
String applicationId = applicationPO.getApplicationId();
// 此处注意事项参见要点2.1
final ApplicationServiceImpl proxy = (ApplicationServiceImpl) AopContext.currentProxy();
proxy.testTransactionNONE(applicationId);
proxy.testTransactionBothDefault(applicationId);
proxy.testTransactionRequiredNew(applicationId);
proxy.testTransactionReadUncommitted(applicationId);
proxy.testTransactionRequiresNewAndReadUncommitted(applicationId);
}
1.2 事务测试方法具体行为(所有测试方法行为一致)
通过原始事务方法传入的
applicationId参数, 尝试在各事务中方法中查询获取此应用记录.
public void testTransactionNONE(String id) {
final ApplicationPO applicationPO = getApplicationById(id);
System.out.println("No Transaction = " + (applicationPO != null));
}
1.3 各测试方法事务属性配置(参见[表1])
-
NONE
public void testTransactionNONE(String id){ -
BOTH_DEFAULT
@Transactional public void testTransactionBothDefault(String id) { -
REQUIRED_NEW
@Transactional(propagation = Propagation.REQUIRES_NEW) public void testTransactionRequiredNew(String id) { -
READ_UNCOMMITTED
@Transactional(isolation = Isolation.READ_UNCOMMITTED) public void testTransactionReadUncommitted(String id) { -
REQUIRES_NEW_READ_UNCOMMITTED
@Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.READ_UNCOMMITTED) public void testTransactionRequiresNewAndReadUncommitted(String id) {
2. 要点
2.1 嵌套调用事务方法
- 事务方法内部嵌套调用另外一个事务方法必须通过代理对象调用, 否则后续方法事务配置不生效, 沿用原始事务.
- 通过
AopContext.currentProxy()获取当前类的代理对象, 所以必须设置expose-proxy为true, 否则抛出异常IllegalStateException. - 由于这几个测试方法不是接口方法, 而是在测试类中直接添加的, 所以该测试类必须通过
cglib代理, 所以必须设置proxy-target-class为true. - 配置标签:
<aop:aspectj-autoproxy expose-proxy="true" proxy-target-class="true"/>
3. 结果分析:
1. NONE : true
没有配置事务, 则沿用上一个事务, 处于同一个事务中所以可读.
2. BOTH_DEFAULT : true
传播级别
propogation=required, 没有事务则创建一个新事务, 若上一个事务存在则沿用上一个事务, 处于同一个事务中所以可读.
3. REQUIRED_NEW : false
传播级别
propogation=required_new, 创建一个新事务, 若上一个事务存在则将上一个事务挂起, 处于不同事务中所以不可读.
4. READ_UNCOMMITTED : true
传播级别
propogation=required, 没有事务则创建一个新事务, 若上一个事务存在则沿用上一个事务, 处于同一个事务中所以可读.此处虽然设置了
READ_UNCOMMITTED, 但由于沿用了上一个事务, 所以不会生效.
5. REQUIRES_NEW_READ_UNCOMMITTED : true
传播级别
propogation=required_new, 创建一个新事务, 若上一个事务存在则将上一个事务挂起. 虽然处于不同事务中默认不可读取消未提交数据, 但此处隔离级别设置为READ_UNCOMMITTED, 允许读取不同事务中的未提交数据.