初学事务管理:SpringBoot+Mybatis

总结一下如何用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("/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("/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("/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("/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;
    }

这次,过程是用与调用者相同的方法编写的,但可以轻松地将其分离到单独的类中。提交和回滚与任何其他用法几乎相同!