Spring事務傳播行爲

本文出處 Spring 事務傳播行爲
轉載請說明出處

在Spring @Transactional 聲明式事務有一個項屬性propagation事務傳播行爲,是一個Propagation 枚舉類,有7種類型,對應不一樣使用場景。下面說下這些枚舉代碼含義,在結合代碼加深理解,鞏固學習。html

Spring 屬性 說明
REQUIRED 支持當前事務,若是不存在事務則建立一個新事務。這個propagation默認屬性
SUPPORTS 支持當前事務,若是不存在事務則按照無事務執行
MANDATORY 支持當前事務,若是不存事務在則拋出異常
REQUIRES_NEW 建立一個新的事務,若是存在當前事務,則將它掛起
NOT_SUPPORTED 無事務執行,若是存在事務將它掛起
NEVER 無事務執行,若是存在當前事務則拋出異常
NESTED 若是當前事務存在,則在嵌套事務中執行,若是不存在事務就像REQUIRED效果

事務傳播級別失效

先作一個小實驗,寫一個持久化的save方法,設置成禁用事務,用事務方法去調用它,看會發生什麼效果。git

@Transactional(propagation = Propagation.NEVER)
    public void save(Users users){
        usersRepository.save(users);
    }

    @Transactional
    public void multSave(){
        Users users = getUsers();
        save(users);
    }

按照上面字段含義解析,save方法不支持事務,在當前事務運行會拋出異常。但是執行結果不是預料那樣,沒有拋出異常,數據寫入到數據庫了。而且咱們能夠在改一次save方法,直接拋出一個RuntimeTime異常,你會發現結果更加明顯。github

@Transactional(propagation = Propagation.NEVER)
    public void save(Users users){
        usersRepository.save(users);
        throw new RuntimeException("4322");
    }

    @Transactional
    public void multSave(){
        Users users = getUsers();
        save(users);
    }

你會發現save 方法 數據回滾了。
amBce1.jpg
在作多幾回這種實驗你就會發現,在同一個類中,最外層事務註解屬性會覆蓋方法內全部事務註解,好比你的外層設置默認事務傳播行爲,不管調用那種行爲都會按照默認屬性,外層沒有設置事務,調用方法有事務也不會生效的。這個不難理解,實現Spring 事務一個基於AOP的代理類,它根據目標方法註解來加強方法,這個是運行時動態執行的,方法內其餘方法沒有代理類的功能加強,就至關於普通方法效果了。千萬不要在同一個類裏面測試事務傳播行爲,不然你永遠都都得不到結論的。spring

進入方法驗證

默認事務

@Transactional
    public void save(){
        Employee employee = new Employee();
        employee.setDepartmentId(2);
        employee.setName("Jorry");
        employee.setSalary(60000);
        repository.save(employee);
        throw new RuntimeException("33333");
    }

    @Transactional
    public void multSave(){
        Users users = getUsers();
        save(users);
        employeeService.save();

    }

執行結果: users、employee寫入失敗,employee的方法拋出異常觸發回滾,也會致使外層方法回滾。在同一個事務中,只要觸發回滾,事務內全部數據操做都會回滾的。數據庫

支持事務

@Transactional(propagation = Propagation.SUPPORTS)
    public void save(){
        Elimployee employee = new Employee();
        employee.setDepartmentId(2);
        employee.setName("Jorry");
        employee.setSalary(60000);
        repository.save(employee);
    }

    @Transactional
    public void multSave(){
        Users users = getUsers();
        save(users);
        employeeService.save();
        throw new RuntimeException("3333");
    }

執行效果: Usres和Elimployee 都進行回滾了。SUPPORTS會加入調用者事務中,和調用者事務是同一個事務,若是在事務中出現異常所有回滾。即便在employeeService.save() 拋出異常,效果同樣的。ide

開啓新事務

@Transactional(propagation = Propagation.REQUIRES_NEW)
    public void save(){
        Employee employee = new Employee();
        employee.setDepartmentId(2);
        employee.setName("Jorry");
        employee.setSalary(60000);
        repository.save(employee);
        throw new RuntimeException("3333");
    }

    @Transactional
    public void multSave(){
        Users users = getUsers();
        save(users);
        try {
            employeeService.save();
        }catch (RuntimeException e){

        }

執行結果: empoyee回滾,users寫入數據庫成功。要處理employeeService.save()異常,否則異常會致使multSave出現異常事務回滾了。加入一個對照,在把employeeService.save()改爲默認屬性,發現users,employee 都會回滾了,還會拋出一個異常學習

org.springframework.transaction.UnexpectedRollbackException: Transaction silently rolled back because it has been marked as rollback-only

Spring官方文檔說到若是內部事務(外部調用者不知道)將事務無提示地標記爲僅回滾,則外部調用者仍會調用commit。外部調用者須要接收一個,UnexpectedRollbackException以清楚地指示已執行回滾。測試

強制不使用事務

@Transactional(propagation = Propagation.NEVER)
    public void save(){
        Employee employee = new Employee();
        employee.setDepartmentId(2);
        employee.setName("Jorry");
        employee.setSalary(60000);
        repository.save(employee);
    }

    @Transactional
    public void multSave(){
        Users users = getUsers();
        save(users);
        employeeService.save();

    }

執行結果: 兩個表都寫入失敗,employeeService.save() 拋出異常ui

org.springframework.transaction.IllegalTransactionStateException: Existing transaction found for transaction marked with propagation 'never'

不支持事務

@Transactional(propagation = Propagation.NOT_SUPPORTED)
    public void save(){
        Employee employee = new Employee();
        employee.setDepartmentId(2);
        employee.setName("Jorry");
        employee.setSalary(60000);
        repository.save(employee);
        throw new RuntimeException("33333");
    }

    @Transactional
    public void multSave(){
        Users users = getUsers();
        save(users);
        employeeService.save();
    }

執行結果:employee成功寫入數據庫,employee 將事務掛了,用無事務方法執行,users受到異常拋出,觸發事務回滾。若是你把employeeService.save()處理一下,兩種表操做都寫入成功了。spa

強制事務

@Transactional(propagation = Propagation.MANDATORY)
    public void save(){
        Employee employee = new Employee();
        employee.setDepartmentId(2);
        employee.setName("Jorry");
        employee.setSalary(60000);
        repository.save(employee);
    }

    public void multSave(){
        Users users = getUsers();
        save(users);
        employeeService.save();
    }

執行結果: users 成功寫入數據庫,employee寫入失敗而且拋出異常

org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory'

嵌套事務

@Transactional
    public void multSave(){
        Users users = getUsers();
        save(users);
        employeeService.save();
    }

    @Transactional(propagation = Propagation.NESTED)
    public void save(){
        Employee employee = new Employee();
        employee.setDepartmentId(2);
        employee.setName("Jorry");
        employee.setSalary(60000);
        repository.save(employee);
    }

執行結果: employeeService.save()拋出一個異常,好像JPA的實現不支持嵌套執行,這個看不到效果了。

org.springframework.transaction.NestedTransactionNotSupportedException: JpaDialect does not support savepoints - check your JPA provider's capabilities
相關文章
相關標籤/搜索