总结一下如何用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; }
|
这次,过程是用与调用者相同的方法编写的,但可以轻松地将其分离到单独的类中。提交和回滚与任何其他用法几乎相同!