SpringBoot事物管理

使用

Controller

@RestController
@RequestMapping("/transaction")
public class TestTransactionController {

    @Autowired
    private TestService testService;

    @RequestMapping(value = "/test", method = RequestMethod.POST)
    public Result test(FrogTest frog) {
        testService.updateAndInsert(frog);
        return new Result();
    }
}

Service

public interface TestService {

    boolean updateAndInsert(FrogTest frog);

}

@Service
public class TestServiceImpl implements TestService {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private IFrogTestService frogService;

    @Override
    public boolean updateAndInsert(FrogTest frog) {
        try {
            frogService.updateById(frog);
            frogService.save(null);
            return true;
        } catch (Exception e) {
            if(logger.isErrorEnabled()) {
                logger.error("updateAndInsert error:", e);
            }
            throw new MyException(MyExceptionEnum.DATASOURCE_ERROR);
        }
    }
}

模擬先修改在添加,update正常執行,insert時會因error sql報錯。如果updateAndInsert方法爲一個事物,只需給該方法加上@Transactional註解即可。

    @Override
    @Transactional
    public boolean updateAndInsert(FrogTest frog) {
        try {
            frogService.updateById(frog);
            frogService.save(null);
            return true;
        } catch (Exception e) {
            if(logger.isErrorEnabled()) {
                logger.error("updateAndInsert error:", e);
            }
            throw new MyException(MyExceptionEnum.DATASOURCE_ERROR);
        }
    }

說明

@Transactional註解應用於類和公開的方法,用於類時,該類中所有公開方法都加入事物管理。回滾RuntimeException和Error,但不回滾已檢查異常。

註解中幾個屬性:
value:與transactionManager相同
transactionManager:指定事物管理器
propagation:事物傳播
isolation:事物隔離
timeout:超時(只使用於REQUIRED和REQUIRES_NEW傳播行爲)
readOnly:是否只讀
rollbackFor:回滾(以下四項的值爲繼承Throwable的類或類名)
rollbackForClassName:類名回滾
noRollbackFor:不回滾
noRollbackForClassName:設置類名不回滾

事物特性ACID
原子性(atomicity):一個事務是一個不可分割的工作單位,事務中包括的諸操作要麼都做,要麼都不做。
一致性(consistency):事務必須是使數據庫從一個一致性狀態變到另一個一致性狀態。一致性與原子性是密切相關的。
隔離性(isolation):一個事務的執行不能被其他事務干擾。即一個事務內部的操作及使用的數據對併發的其他事務是隔離的,併發執行的各個事務之間不能互相干擾。
持久性(durability):持續性也稱永久性(permanence),指一個事務一旦提交,它對數據庫中數據的改變就應該是永久性的。接下來的其他操作或故障不應該對其有任何影響。

事物傳播行爲
REQUIRED:支持當前事物,如果不存在則創建一個事物。(默認值)
SUPPORTS:支持當前事務,如果不存在則以非事務方式執行。
MANDATORY:支持當前事務,如果不存在則拋出異常。
REQUIRES_NEW:創建一個新事務,並暫停當前事務(如果存在)。
NOT_SUPPORTED:以非事務方式執行,暫停當前事務(如果存在)。
NEVER:則以非事務方式執行,如果事務存在,拋出異常。
NESTED:如果當前存在事務,則創建一個事務作爲當前事務的嵌套事務來運行;如果當前沒有事務,則該取值等價於 REQUIRED

事物隔離級別
DEFAULT:使用基礎數據存儲的默認隔離級別。(默認值)
READ_UNCOMMITTED:該隔離級別表示一個事務可以讀取另一個事務修改但還沒有提交的數據。如果發生回滾,則第二個事務將檢索到無效行。該級別不能防止髒讀和不可重複讀,因此很少使用該隔離級別。
READ_COMMITTED:該隔離級別表示一個事務只能讀取另一個事務已經提交的數據。該級別可以防止髒讀,這也是大多數情況下的推薦值。
REPEATABLE_READ:該隔離級別表示一個事務在整個過程中可以多次重複執行某個查詢,並且每次返回的記錄都相同。即使在多次查詢之間有新增的數據滿足該查詢,這些新增的記錄也會被忽略。該級別可以防止髒讀和不可重複讀。
SERIALIZABLE:所有的事務依次逐個執行,這樣事務之間就完全不可能產生干擾,也就是說,該級別可以防止髒讀、不可重複讀以及幻讀。但是這將嚴重影響程序的性能。通常情況下也不會用到該級別。

這裏引用其他作者描述對髒讀、不可重複讀、幻讀的理解:


作者:Somhu
原文:https://blog.csdn.net/Somhu/article/details/78775198?utm_source=copy

髒讀:所謂的髒讀,其實就是讀到了別的事務回滾前的髒數據。比如事務B執行過程中修改了數據X,在未提交前,事務A讀取了X,而事務B卻回滾了,這樣事務A就形成了髒讀。
也就是說,當前事務讀到的數據是別的事務想要修改成爲的但是沒有修改成功的數據。
不可重複讀:事務A首先讀取了一條數據,然後執行邏輯的時候,事務B將這條數據改變了,然後事務A再次讀取的時候,發現數據不匹配了,就是所謂的不可重複讀了。
也就是說,當前事務先進行了一次數據讀取,然後再次讀取到的數據是別的事務修改成功的數據,導致兩次讀取到的數據不匹配,也就照應了不可重複讀的語義。
幻讀:事務A首先根據條件索引得到N條數據,然後事務B改變了這N條數據之外的M條或者增添了M條符合事務A搜索條件的數據,導致事務A再次搜索發現有N+M條數據了,就產生了幻讀。
也就是說,當前事務讀第一次取到的數據比後來讀取到數據條目少。


注意的問題

Service

    @Override
    @Transactional
    public boolean updateAndInsert(FrogTest frog) {
        try {
            frogService.updateById(frog);
            frogService.save(null);
            return true;
        } catch (Exception e) {
            if(logger.isErrorEnabled()) {
                logger.error("updateAndInsert error:", e);
            }
            throw new MyException(MyExceptionEnum.DATASOURCE_ERROR);
        }
    }

    @Override
    public void update(FrogTest frog) {
        updateAndInsert(frog);
    }

如果將Service層修改爲如上,Controller層先調用update方法,update在調用updateAndInsert方法,儘管後者加了@Transactional註解,方法中兩個操作並不是一個事物。
因爲@Transactional 的事務開啓 ,或者是基於接口的或者是基於類的代理被創建。所以在同一個類中一個方法調用另一個方法有事務的方法,事務是不會起作用的。
參考:https://zhuanlan.zhihu.com/p/38208248
經過測試,update方法加上註解,updateAndInsert不加,事物起作用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章