Spring 事務管理

事務

Github自動化測試的緣由,(最後找到的緣由是getOneSavedDepartment時,這個Department沒存上,因此ToMessage引用了一個未持久化的Department,就報錯了),特此學習了一下事務。java

事務,基礎概念就不說了。spring

Spring爲咱們提供了對事務的支持,咱們只須要很簡單的註解或者XML配置便可實現。ide

去網上找了好多篇關於Spring事務的博客,全是字,根本沒有心情去看,更談不上深刻理解了,做者還在標題中自認爲本身講的比較好。學習

clipboard.png

若是你也不喜歡大段的文字,請繼續向下看,我保證我畫的圖不會讓你失望。測試

org.springframework.transaction.annotation.Transactional

@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

這裏咱們深刻學習一下事務的傳播屬性propagationcode

Propagation

REQUIRED

由於咱們的事務傳播級別就是REQUIRED,因此咱們不配置propagation來測試REQUIREDblog

若是當前存在事務,則使用當前事務。若是不存在任何事務,則建立一個新的事務。

內部方法開啓默認REQUIRED級別的事務

一個🐱,一個🐩,省略setget方法。事務

@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);
}

沒有任何異常拋出,貓存上了,狗也存上了。

clipboard.png

clipboard.png

將保存狗的方法由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);
}

clipboard.png

clipboard.png

貓存上了,狗沒存上。由於同一事務的全部操做是同時成功,同時失敗的。因此咱們判定,貓和狗用的是兩個事務。

圖解

clipboard.png

若是不存在任何事務,則建立一個新的事務。

save貓save狗都是默認的REQUIRED級別,test方法未開啓事務,因此當前不存在任何事務。

因此save貓建立了一個新的事務,save狗也建立了一個新的事務。

clipboard.png

外部方法開啓事務

爲外部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);
}

顯然,未拋出異常,都存上了。

clipboard.png

clipboard.png

方法修改成保存並拋出異常。

@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貓是有影響的,猜想兩者用的是同一個事務。

clipboard.png

clipboard.png

若是當前存在事務,則使用當前事務。

clipboard.png

若是捕獲拋出的RuntimeException,該方法仍然不能保存。

Dog dog = new Dog();
dog.setName("史努比");
try {
    dogService.saveThrowException(dog);
} catch (RuntimeException e) {
    System.out.println("error");
}

會拋出異常,該事務不能被提交。

clipboard.png

clipboard.png

clipboard.png

總結:REQUIRED修飾的內部方法會加入外部方法的事務中,其中有任一一個方法拋異常,事務都會回滾。

SUPPORTS

SUPPORTSREQUIRED相似:

若是當前存在事務,則使用當前事務。若是不存在任何事務,則不使用事務。

MANDATORY

MANDATORYREQUIRED相似:

若是當前存在事務,則使用當前事務。若是不存在任何事務,則拋出異常。

REQUIRES_NEW

若是當前存在事務,則掛起當前事務。建立一個新的事務。

應該適合該操做相對獨立的狀況。就好比下單加支付,若是支付失敗,可是這個訂單應該還存在。

若是將事務傳播級別修改成這個的話,那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();
}

clipboard.png

clipboard.png

NOT_SUPPORTED

若是當前存在事務,則掛起當前事務。同時以非事務方式運行。

NEVER

永遠不要存在事務。若是當前存在事務,則拋出異常。

NESTED

若是當前存在事務,則在當前事務的一個嵌套事務中運行。

該級別只對DataSourceTransactionManager事務管理器生效。

原本想測試一下的,惋惜。

clipboard.png

org.springframework.transaction.NestedTransactionNotSupportedException: JpaDialect does not support savepoints - check your JPA provider's capabilities

NestedTransactionNotSupportedException,不支持嵌套事務。

StackOverflow上的回答,Hibernate不支持嵌套事務。

clipboard.png

總結

  1. 哥仨REQUIREDSUPPORTSMANDATORY,若是當前存在事務,則使用當前事務。
  2. 哥仨REQUIRES_NEWNOT_SUPPORTEDNEVER都不支持當前存在的事務。
  3. NESTED,嵌套事務,以爲這個應該是事務中最好用且最合理的,惋惜Hibernate不支持。
相關文章
相關標籤/搜索