事务因Github自动化测试的原因,(最后找到的原因是getOneSavedDepartment时,这个Department没存上,所以ToMessage引用了一个未持久化的Department,就报错了),特此学习了一下事务。事务,基础概念就不说了。Spring为我们提供了对事务的支持,我们只需要很简单的注解或者XML配置即可实现。去网上找了好多篇关于Spring事务的博客,全是字,根本没有心情去看,更谈不上深入理解了,作者还在标题中自认为自己讲的比较好。如果你也不喜欢大段的文字,请继续向下看,我保证我画的图不会让你失望。org.springframework.transaction.annotation.Transactional@Target({ElementType.METHOD, ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Inherited@Documentedpublic @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 {};}事务注解中有好多的属性,如果是简单场景的话,那我们只需要默认地配置就好了。但是如果应用发嵌套事务的复杂场景下,我们就需要研究研究这几个配置项了。这里我们深入学习一下事务的传播属性propagation。PropagationREQUIRED因为我们的事务传播级别就是REQUIRED,所以我们不配置propagation来测试REQUIRED。如果当前存在事务,则使用当前事务。如果不存在任何事务,则创建一个新的事务。内部方法开启默认REQUIRED级别的事务一个????,一个????,省略set、get方法。@Entitypublic class Cat { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String name;}@Entitypublic class Dog { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String name;}一个猫实现类,一个狗实现类,都有save方法,并且DogServiceImpl中还有一个保存并抛出异常的方法,用于测试回滚。省略依赖注入的代码。@Servicepublic class CatServiceImpl implements CatService { @Override @Transactional public void save(Cat cat) { catRepository.save(cat); }}@Servicepublic 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方法添加事务。@Transactionalpublic void test() { Cat cat = new Cat(); cat.setName(“Hello Kitty!”); catService.save(cat); Dog dog = new Dog(); dog.setName(“史努比”); dogService.save(dog);}显然,未抛出异常,都存上了。方法修改为保存并抛出异常。@Transactionalpublic 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修饰的内部方法会加入外部方法的事务中,其中有任一一个方法抛异常,事务都会回滚。SUPPORTSSUPPORTS与REQUIRED类似:如果当前存在事务,则使用当前事务。如果不存在任何事务,则不使用事务。MANDATORYMANDATORY与REQUIRED类似:如果当前存在事务,则使用当前事务。如果不存在任何事务,则抛出异常。REQUIRES_NEW如果当前存在事务,则挂起当前事务。创建一个新的事务。应该适合该操作相对独立的情况。就比如下单加支付,如果支付失败,但是这个订单应该还存在。如果将事务传播级别修改为这个的话,那save狗如果抛出异常就不影响save猫了。@Override@Transactionalpublic 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();}NOT_SUPPORTED如果当前存在事务,则挂起当前事务。同时以非事务方式运行。NEVER永远不要存在事务。如果当前存在事务,则抛出异常。NESTED如果当前存在事务,则在当前事务的一个嵌套事务中运行。该级别只对DataSourceTransactionManager事务管理器生效。本来想测试一下的,可惜。org.springframework.transaction.NestedTransactionNotSupportedException: JpaDialect does not support savepoints - check your JPA provider’s capabilitiesNestedTransactionNotSupportedException,不支持嵌套事务。StackOverflow上的回答,Hibernate不支持嵌套事务。总结哥仨REQUIRED、SUPPORTS、MANDATORY,如果当前存在事务,则使用当前事务。哥仨REQUIRES_NEW、NOT_SUPPORTED、NEVER都不支持当前存在的事务。NESTED,嵌套事务,觉得这个应该是事务中最好用且最合理的,可惜Hibernate不支持。