因Github
自动化测试的缘由,(最后找到的缘由是getOneSavedDepartment
时,这个Department
没存上,因此ToMessage
引用了一个未持久化的Department
,就报错了),特此学习了一下事务。java
事务,基础概念就不说了。spring
Spring
为咱们提供了对事务的支持,咱们只须要很简单的注解或者XML
配置便可实现。ide
去网上找了好多篇关于Spring
事务的博客,全是字,根本没有心情去看,更谈不上深刻理解了,做者还在标题中自认为本身讲的比较好。学习
若是你也不喜欢大段的文字,请继续向下看,我保证我画的图不会让你失望。测试
@Target({ElementType.METHOD, ElementType.TYPE}) @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 -1; boolean readOnly() default false; Class<? extends Throwable>[] rollbackFor() default {}; String[] rollbackForClassName() default {}; Class<? extends Throwable>[] noRollbackFor() default {}; String[] noRollbackForClassName() default {}; }
事务注解中有好多的属性,若是是简单场景的话,那咱们只须要默认地配置就行了。spa
可是若是应用发嵌套事务的复杂场景下,咱们就须要研究研究这几个配置项了。3d
这里咱们深刻学习一下事务的传播属性propagation
。code
由于咱们的事务传播级别就是REQUIRED
,因此咱们不配置propagation
来测试REQUIRED
。blog
若是当前存在事务,则使用当前事务。若是不存在任何事务,则建立一个新的事务。
一个🐱,一个🐩,省略set
、get
方法。事务
@Entity public class Cat { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String name; } @Entity public class Dog { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String name; }
一个猫实现类,一个狗实现类,都有save
方法,而且DogServiceImpl
中还有一个保存并抛出异常的方法,用于测试回滚。省略依赖注入的代码。
@Service public class CatServiceImpl implements CatService { @Override @Transactional public void save(Cat cat) { catRepository.save(cat); } } @Service public class DogServiceImpl implements DogService { @Override @Transactional public void save(Dog dog) { dogRepository.save(dog); } @Override @Transactional public void saveThrowException(Dog dog) { dogRepository.save(dog); throw new RuntimeException(); } }
public void test() { Cat cat = new Cat(); cat.setName("Hello Kitty!"); catService.save(cat); Dog dog = new Dog(); dog.setName("史努比"); dogService.save(dog); }
没有任何异常抛出,猫存上了,狗也存上了。
将保存狗的方法由save
修改成saveThrowException
,抛个异常,测试一下回滚。
public void test() { Cat cat = new Cat(); cat.setName("Hello Kitty!"); catService.save(cat); Dog dog = new Dog(); dog.setName("史努比"); dogService.saveThrowException(dog); }
猫存上了,狗没存上。由于同一事务的全部操做是同时成功,同时失败的。因此咱们判定,猫和狗用的是两个事务。
图解
若是不存在任何事务,则建立一个新的事务。
save猫
和save狗
都是默认的REQUIRED
级别,test
方法未开启事务,因此当前不存在任何事务。
因此save猫
建立了一个新的事务,save狗
也建立了一个新的事务。
为外部test
方法添加事务。
@Transactional public void test() { Cat cat = new Cat(); cat.setName("Hello Kitty!"); catService.save(cat); Dog dog = new Dog(); dog.setName("史努比"); dogService.save(dog); }
显然,未抛出异常,都存上了。
方法修改成保存并抛出异常。
@Transactional public void test() { Cat cat = new Cat(); cat.setName("Hello Kitty!"); catService.save(cat); Dog dog = new Dog(); dog.setName("史努比"); dogService.saveThrowException(dog); }
二者都没存上,因此判定save狗
的方法抛出的异常对save猫
是有影响的,猜想两者用的是同一个事务。
若是当前存在事务,则使用当前事务。
若是捕获抛出的RuntimeException
,该方法仍然不能保存。
Dog dog = new Dog(); dog.setName("史努比"); try { dogService.saveThrowException(dog); } catch (RuntimeException e) { System.out.println("error"); }
会抛出异常,该事务不能被提交。
总结:REQUIRED
修饰的内部方法会加入外部方法的事务中,其中有任一一个方法抛异常,事务都会回滚。
SUPPORTS
与REQUIRED
相似:
若是当前存在事务,则使用当前事务。若是不存在任何事务,则不使用事务。
MANDATORY
与REQUIRED
相似:
若是当前存在事务,则使用当前事务。若是不存在任何事务,则抛出异常。
若是当前存在事务,则挂起当前事务。建立一个新的事务。
应该适合该操做相对独立的状况。就好比下单加支付,若是支付失败,可是这个订单应该还存在。
若是将事务传播级别修改成这个的话,那save狗
若是抛出异常就不影响save猫
了。
@Override @Transactional public void test() { Cat cat = new Cat(); cat.setName("Hello Kitty!"); catService.save(cat); Dog dog = new Dog(); dog.setName("史努比"); dogService.saveThrowException(dog); }
@Override @Transactional(propagation = Propagation.REQUIRES_NEW) public void save(Cat cat) { catRepository.save(cat); } @Override @Transactional(propagation = Propagation.REQUIRES_NEW) public void saveThrowException(Dog dog) { dogRepository.save(dog); throw new RuntimeException(); }
若是当前存在事务,则挂起当前事务。同时以非事务方式运行。
永远不要存在事务。若是当前存在事务,则抛出异常。
若是当前存在事务,则在当前事务的一个嵌套事务中运行。
该级别只对DataSourceTransactionManager
事务管理器生效。
原本想测试一下的,惋惜。
org.springframework.transaction.NestedTransactionNotSupportedException: JpaDialect does not support savepoints - check your JPA provider's capabilities
NestedTransactionNotSupportedException
,不支持嵌套事务。
StackOverflow
上的回答,Hibernate
不支持嵌套事务。
REQUIRED
、SUPPORTS
、MANDATORY
,若是当前存在事务,则使用当前事务。REQUIRES_NEW
、NOT_SUPPORTED
、NEVER
都不支持当前存在的事务。NESTED
,嵌套事务,以为这个应该是事务中最好用且最合理的,惋惜Hibernate
不支持。