我在工作中经常会遇到使用事务来控制哪些方法需要在一起提交以保证数据的一致性问题。以下将以程序例子来介绍事务的传播属性的回滚方式,主要演示REQUIRED和、REQUIRES_NEW两种传播属性,testTransaction()调用testTransaction1()和testTransaction2()
(以spring注解方式来声明事务)
1. REQUIRED
此种传播属性是,如果当前上下文有事务就加入,没有事务就新建。是最常用的一种。
看测试例子:
@Transactional(propagation=Propagation.REQUIRED)
public void testTransaction1(){
DictTempEntity entity = new DictTempEntity();
entity.setDicType("01");
entity.setDicState("01");
entity.setDicKeyDesc("测试");
dicTempRepository.save(entity);
}
@Transactional(propagation=Propagation.REQUIRED)
public void testTransaction2(){
DictTempEntity entity = new DictTempEntity();
entity.setDicType("02");
entity.setDicState("02");
entity.setDicKeyDesc("测试");
dicTempRepository.save(entity);
}
@Override
@Transactional(propagation=Propagation.REQUIRED)
public void testTransaction(){
this.testTransaction1();
this.testTransaction2();
throw new NullPointerException();
}
程序走到了testTransaction打开了事务,走到testTransaction1的时候,加入到该事物,走到testTransaction2也是加入到该事物中,当testTransaction1和testTransaction2运行完毕后,testTransaction发生了异常,此时由于在同一事务中,testTransaction1和testTransaction2并未提交,数据库中没有保存入此2条记录。
那么同样,如果在testTransaction2中发生了异常,testTransaction1虽然已经执行完,由于这3个方法在同一个事务中,testTransaction1中的也将回滚。
2.REQUIRES_NEW
如果上下文中有事务了,那么使当前事务挂起,自己新建事务。子事务会单独提交。
@Override
@Transactional(propagation=Propagation.REQUIRED)
public void testTransaction(){
this.bankAccountService.testTransaction1();
this.bankAccountService.testTransaction2();
DictTempEntity entity = new DictTempEntity();
entity.setDicType("03");
entity.setDicState("03");
entity.setDicKeyDesc("测试");
dicTempRepository.save(entity);
throw new NullPointerException();
}
@Override
@Transactional(propagation=Propagation.REQUIRES_NEW)
public void testTransaction1(){
DictTempEntity entity = new DictTempEntity();
entity.setDicType("01");
entity.setDicState("01");
entity.setDicKeyDesc("测试");
dicTempRepository.save(entity);
}
@Override
@Transactional(propagation=Propagation.REQUIRES_NEW)
public void testTransaction2(){
DictTempEntity entity = new DictTempEntity();
entity.setDicType("02");
entity.setDicState("02");
entity.setDicKeyDesc("测试");
dicTempRepository.save(entity);
}
在这个测试中,testTransaction已经新建了事务,当运行到testTransaction1的时候,由于是requires_new,那么使当前事务挂起新建事务,同样testTransaction2也是,此时在testTransaction中发生了异常,由于两个子事务是单独的已经提交,所以01、02两条数据已经提交至数据库,03数据没有提交。
注意:使用这种方式的两个子事务不能和父事务在一个类中,否则不会新建事务,不会生效!
同样,如果在testTransaction2中发生了异常,代码改成如下:
@Override
@Transactional(propagation=Propagation.REQUIRED)
public void testTransaction(){
this.bankAccountService.testTransaction1();
try{
this.bankAccountService.testTransaction2();
}catch(Exception e){
LOGGER.info("发生异常");
}
DictTempEntity entity = new DictTempEntity();
entity.setDicType("03");
entity.setDicState("03");
entity.setDicKeyDesc("测试");
dicTempRepository.save(entity);
//throw new NullPointerException();
}
@Override
@Transactional(propagation=Propagation.REQUIRES_NEW)
public void testTransaction1(){
DictTempEntity entity = new DictTempEntity();
entity.setDicType("01");
entity.setDicState("01");
entity.setDicKeyDesc("测试");
dicTempRepository.save(entity);
}
@Transactional(propagation=Propagation.REQUIRES_NEW)
public void testTransaction2(){
DictTempEntity entity = new DictTempEntity();
entity.setDicType("02");
entity.setDicState("02");
entity.setDicKeyDesc("测试");
dicTempRepository.save(entity);
throw new NullPointerException();
}
由于父子事务分别是单独的,所以只要程序能正常进行,testTransaction1和testTransaction都能提交,也就是01、03都有数据。
3.NESTED
嵌套事务,有个savepoint,子事务发生异常回滚到父事务的savePoint,子事务回滚不影响父事务,但是父事务回滚子事务跟着回滚。
4. NOT_SUPPORT
将当前逻辑排除在上下文事务之外,用于缩小事务的范围以保证安全,比如有一些非核心的业务逻辑就可以用这种情况。
5. MANDATORY
上下文中必须有事务,否则抛异常。此种方法不常见,可以用于这段代码必须在一个事务中进行的情况,但是其实只要将这段代码放在一个事务中就可以了,比如用required,不用非要以抛出异常的方式,所以不常见.
6. SUPPORT
支持当前事务,如果当前没事务,就以非事务方式进行。
7.NEVER
以非事务方式执行,如果有事务,就抛出异常,这种用法也比较少。
参考文章:http://sharajava.iteye.com/blog/78270