初學事務管理:SpringBoot+Mybatis

banq發表於2024-06-16

總結一下如何用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;
    }

這次,過程是用與呼叫者相同的方法編寫的,但可以輕鬆地將其分離到單獨的類中。提交和回滾與任何其他用法幾乎相同!


 

相關文章