1. 程式人生 > >程式碼丟擲異常後進行事務回滾的兩種方式(Spring @Transactional註解)

程式碼丟擲異常後進行事務回滾的兩種方式(Spring @Transactional註解)

需求

在service層的某個方法中,在執行完一個對資料庫的寫方法後,丟擲異常,再執行另一個對資料庫的寫方法,虛擬碼如下:

@Transactional
public void func() {
	dao.write(pojo1);
	throw new Exception("異常");
	dao.write(pojo2);
}

要求此時事務全部回滾,即pojo1和pojo2都不寫進資料庫。

單元測試程式碼

    @Test
    public void testTransactional() {
        User user = new User();
        user.setUid(9);
        user.setUsername("李四");
        user.setPassword("234");
        userService.createAndUpdate(user);
    }

不回滾寫法

這種寫法保留了createUser方法對資料庫的寫操作,而不執行UpdateUser方法,結果是把建立的User物件李四寫進了資料庫,但並沒有重新命名為admin。原因是丟擲異常並捕獲後,並沒有觸發事務回滾。所以程式碼結束後李四保留在了資料庫中。

    @Override
    public void createAndUpdate(User user) {
        try {
            createUser(user);
            if (!user.getPassword().equals("123")) {
                throw new RuntimeException("密碼不是123");
            }
            user.setUsername("admin");
            updateUser(user);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

回滾寫法

寫法一:方法拋異常

這種寫法可以在方法處丟擲異常,也可以不丟擲(throws RuntimeException可寫可不寫)。

    @Override
    public void createAndUpdate(User user) throws RuntimeException {
        createUser(user);
        if (!user.getPassword().equals("123")) {
            throw new RuntimeException("密碼不是123");
        }
        user.setUsername("admin");
        updateUser(user);
    }

寫法二:try catch捕獲異常

這種方法相比於不回滾的那種寫法,只是在catch作用域內多加入了一行程式碼:

TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();

完整方法:

    @Override
    public void createAndUpdate(User user) {
        try {
            createUser(user);
            if (!user.getPassword().equals("123")) {
                throw new RuntimeException("密碼不是123");
            }
            user.setUsername("admin");
            updateUser(user);
        } catch (Exception e) {
            e.printStackTrace();
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
    }