因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
不支持。