總結一下如何用SpringBoot和Mybatis來管理事務! !
到底什麼是事務?
事務管理是任何專案都無法避免的基本要素。如果你能明白這一點,你作為工程師的水平將會顯著提高!也許!另一方面,如果你不理解它,它就會成為bug的溫床,所以這次我們決定在原始碼中顯式地編寫事務管理程式碼!這是一個基本的方法!
簡而言之,事務就是“確保資料完整性”。從應用程式運算元據庫以搜尋、註冊、更新和刪除表資料。在此過程中,可能會發生意外錯誤,並且可能不得不中斷該過程。如果當時執行事務管理,則可以將 DB 資料返回到處理開始之前的狀態。
SpringBoot中如何管理事務
使用SpringBoot管理事務時,主要有以下幾種方法。
- 使用 PlatformTransactionManager 類
- 使用 SqlSession 類
- 使用 TransactionTemplate 類
眼見為實,所以讓我們首先看一下我們建立的示例原始碼!如果您訪問 http://localhost:8080/tran/addplat 等,它就可以工作。這次,我確保生成 RuntimeException 並回滾,以便發生錯誤。
package com.example.demo;
import java.time.LocalDateTime; import java.util.HashMap; import java.util.Map;
import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.DefaultTransactionDefinition; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;
import com.example.demo.domain.Userhi; import com.example.demo.repo.UserhiRepo;
@RestController @RequestMapping(<font>"/tran") public class Util { @Autowired PlatformTransactionManager txManager; @Autowired SqlSessionFactory factory; @Autowired UserhiRepo userRepo;
@GetMapping("/addplat") public Map<String, String> plat() { final Map<String, String> map = new HashMap<String, String>(); DefaultTransactionDefinition def = new DefaultTransactionDefinition(); def.setName("HiguTran"); def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
TransactionStatus status = txManager.getTransaction(def); try { LocalDateTime now = LocalDateTime.now(); Userhi u = new Userhi(); u.setUserEmail("higuhigu@tran.com"); u.setUserName("higutran"); u.setDelFlg(0); u.setUserRegDatetime(now); u.setUserUpdateStamp(now); userRepo.create(u); boolean res = true; if (res) { throw new RuntimeException(); } } catch (Exception ex) { txManager.rollback(status); map.put("PlatformTransactionManager error", "error PlatformTransactionManager!");
return map;
} txManager.commit(status); return map;
}
@GetMapping("/addsql") public Map<String, String> sql() { final Map<String, String> map = new HashMap<String, String>();
SqlSession session = factory.openSession(); try { UserhiRepo ur = session.getMapper(UserhiRepo.class); LocalDateTime now = LocalDateTime.now(); Userhi u = new Userhi(); u.setUserEmail("higuhigu@tran.com"); u.setUserName("higutran"); u.setDelFlg(0); u.setUserRegDatetime(now); u.setUserUpdateStamp(now); userRepo.create(u); boolean res = true; if (res) { throw new RuntimeException(); } session.commit(); } catch(Exception e ) { session.rollback(); map.put("SqlSession error", "error SqlSession!"); } finally { session.close(); }
return map; } @GetMapping("/addtran") public Map<String, String> tran() { final Map<String, String> map = new HashMap<String, String>(); TransactionTemplate temp = new TransactionTemplate(txManager); temp.execute(new TransactionCallbackWithoutResult() { protected void doInTransactionWithoutResult(TransactionStatus status) { try { LocalDateTime now = LocalDateTime.now(); Userhi u = new Userhi(); u.setUserEmail("higuhigu@tran.com"); u.setUserName("higutran"); u.setDelFlg(0); u.setUserRegDatetime(now); u.setUserUpdateStamp(now); userRepo.create(u); if (true) { throw new RuntimeException(); }
} catch (Exception e) { status.setRollbackOnly(); map.put("TransactionTemplate error", "error TransactionTemplate!"); }
} }); return map; } }
|
使用PlatformTransactionManager類的過程就是上面示例中的plat ()方法。上面示例中的sql ()方法
是使用SqlSession類的過程。上面示例中的tran () 方法使用 TransactionTemplate 類。平臺事務管理器
這是Spring提供的一個事務管理模組。根據Spring的官方文件,似乎不建議單獨使用PlatformTransactionManager。建議和TransactionTemplate結合使用:
我們來看看實際使用PlatformTransactionManager的原始碼!
@GetMapping(<font>"/addplat") public Map<String, String> plat() { final Map<String, String> map = new HashMap<String, String>(); DefaultTransactionDefinition def = new DefaultTransactionDefinition(); def.setName("HiguTran"); def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
TransactionStatus status = txManager.getTransaction(def); try { LocalDateTime now = LocalDateTime.now(); Userhi u = new Userhi(); u.setUserEmail("higuhigu@tran.com"); u.setUserName("higutran"); u.setDelFlg(0); u.setUserRegDatetime(now); u.setUserUpdateStamp(now); userRepo.create(u); boolean res = true; if (res) { throw new RuntimeException(); } } catch (Exception ex) { txManager.rollback(status); map.put("PlatformTransactionManager error", "error PlatformTransactionManager!"); return map; } txManager.commit(status); return map; }
|
事務管理透過txManager.getTransaction(def)啟動(也稱為啟動事務!)。如果沒有發生錯誤,則使用txManager.commit(status)插入的結果將被儲存!如果發生錯誤並被捕獲,將使用txManager.rollback(status)回滾資料。這很容易。
順便問一下,def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED)是做什麼的?我相信你們中的一些人可能會這麼認為,所以我將簡要解釋一下,因為它很重要。這些設定稱為事務傳播屬性。
傳播屬性
簡而言之,它是一種“事務管理方法”。隨著處理變得更大、更復雜,可能會出現多個事務。因此,當您啟動事務時,事務可能已經開始。決定當時將執行何種事務管理的設定成為傳播屬性。
以下是交易傳播屬性的示例!
我將在這裡解釋兩件事。一種是PROPAGATION_REQUIRED ,它位於示例原始碼中。如果啟動交易時現有交易已經存在,這將使用現有交易!如果沒有,請建立一個新交易!
還有PROPAGATION_REQUIRES_NEW 。如果指定,即使事務已經存在,也會建立一個新事務。
區別在於發生錯誤時的行為。
- PROPAGATION_REQUIRED返回到過程 1 中事務開始之前的狀態,即使過程 1 或過程 2中發生錯誤!
- 對於PROPAGATION_REQUIRES_NEW,如果過程 1 發生錯誤,而過程 2 正常結束,則只有程序 1 會返回到事務開始之前的狀態。過程2中所做的更改將不會被退回!
通常,我覺得在一個過程中只准備一個事務是一條黃金法則
會話
SqlSession是MyBatis-Spring庫中的一個事務管理類。基本上我認為這是MyBatis準備的一個類,另外一個可能會更好!我們來實際看看原始碼吧!
@GetMapping(<font>"/addsql") public Map<String, String> sql() { final Map<String, String> map = new HashMap<String, String>();
SqlSession session = factory.openSession(); try { UserhiRepo ur = session.getMapper(UserhiRepo.class); LocalDateTime now = LocalDateTime.now(); Userhi u = new Userhi(); u.setUserEmail("higuhigu@tran.com"); u.setUserName("higutran"); u.setDelFlg(0); u.setUserRegDatetime(now); u.setUserUpdateStamp(now); userRepo.create(u); boolean res = true; if (res) { throw new RuntimeException(); } session.commit(); } catch(Exception e ) { session.rollback(); map.put("SqlSession error", "error SqlSession!"); } finally { session.close(); }
return map; }
|
使用方法沒有太大變化!在上面的原始碼中,事務是透過factory.openSession()啟動的,並透過session.commit()提交的!如果發生錯誤,則使用 session.rollback() 進行回滾。
事務模板
最後,如何使用TransactionTemplate。我們簡單介紹瞭如何使用 PlatformTransactionManager,但 TransactionTemplate 是 PlatformTransactionManager 的便捷版本!我們來看看原始碼吧!
@Autowired PlatformTransactionManager txManager;
@GetMapping(<font>"/addtran") public Map<String, String> tran() { final Map<String, String> map = new HashMap<String, String>(); TransactionTemplate temp = new TransactionTemplate(txManager); temp.execute(new TransactionCallbackWithoutResult() { protected void doInTransactionWithoutResult(TransactionStatus status) { try { LocalDateTime now = LocalDateTime.now(); Userhi u = new Userhi(); u.setUserEmail("higuhigu@tran.com"); u.setUserName("higutran"); u.setDelFlg(0); u.setUserRegDatetime(now); u.setUserUpdateStamp(now); userRepo.create(u); if (true) { throw new RuntimeException(); } } catch (Exception e) { status.setRollbackOnly(); map.put("TransactionTemplate error", "error TransactionTemplate!"); } } }); return map; }
|
這次,過程是用與呼叫者相同的方法編寫的,但可以輕鬆地將其分離到單獨的類中。提交和回滾與任何其他用法幾乎相同!