springboot中手动提交事务的实现方法

 更新时间:2024年01月07日 10:00:31   作者:爱看老照片  
手动提交事务可以提供更灵活的控制,以便在分布式环境中处理事务的提交和回滚,本文就来介绍一下springboot中手动提交事务的实现方法,感兴趣的可以了解一下

演示主要代码

@Service 层代码

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.DefaultTransactionDefinition;

@Service
@Transactional(rollbackFor = Exception.class)
public class XlServiceImpl {
	Logger logger = LoggerFactory.getLogger(this.getClass());
	@Autowired
	private XlMapper xlMapper;
	
	/**
	 * 需求:mi()方法抛出异常,不影响本方法:本方法不回滚!
	 * mi抛出异常后,insert1()正常插入
	 * @param id
	 * @return
	 * @throws Exception
	 */
	public String doInsert() throws Exception {
		xlMapper.insert1();
		mi();
		return "200";
	}
	
	/**
	 * 需求:本方法抛出异常时,回滚
	 * 抛出异常后,insert2()回滚:不插入
	 */
	private void mi() {
		xlMapper.insert2();
		int x = 0;
		int y = 3 / x; // 
	}

}

Mapper接口层代码: insert1()和insert2()的插入SQL

import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Update;

public interface XlMapper {

    @Update("UPDATE xl999 SET age=333 WHERE id=#{id}")
	Integer updateById(Integer id);
    
    @Insert("INSERT INTO xl (name,age,create_time) VALUES('dp1',111,NOW())")
    Integer insert1();
    
    @Insert("INSERT INTO xl (name,age,create_time) VALUES('dp2',222,NOW())")
    Integer insert2();
}

controller层代码

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ClassForTest {
	Logger logger = LoggerFactory.getLogger(this.getClass());

	@Autowired
	private XlServiceImpl xlServiceImpl;

	@GetMapping("/dosth")
	public String doSth() throws Exception {
		String affect = "";
		affect = xlServiceImpl.doInsert();
		return affect;
	}
}

数据库表结构及初始数据

在这里插入图片描述

场景/需求/实际效果

场景
在spring的声明式事务@Transactional(rollbackFor = Exception.class)的类XlServiceImpl中:

  • 有其中一个方法doInsert()调用另外一个方法mi()
  • doInsert()会调用Mapper接口的insert1()方法向数据库中插入一条数据,然后会调用方法mi()。
  • mi()会调用Mapper接口的insert2()方法向数据库中插入一条数据,然后会抛出异常。

需求

insert1()可以正常插入,insert2()回滚,不会插入!

实际效果
运行项目,调用方法,结果如下:

页面:

在这里插入图片描述

程序后台

在这里插入图片描述

数据库: 与初始数据库数据一致,并没有数据插入——与需求中的 insert1()成功插入不符合。

在这里插入图片描述

解决办法 :在mi方法中手动提交事务

  • 在@Transactional(rollbackFor = Exception.class)类中注入spring的事务管理器PlatformTransactionManager:
	/**
	 * 引入 (平台)事务管理器,Spring 事务策略的核心。
	 */
	@Autowired
	private PlatformTransactionManager transactionManager;
  • 在mi方法中手动提交事务/回滚事务
	/**
	 * 需求:本方法抛出异常时,回滚 抛出异常后,insert2()回滚:不插入
	 */
	private void mi() {
		DefaultTransactionDefinition def = new DefaultTransactionDefinition();
		def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);// 新发起一个事务
		TransactionStatus status = transactionManager.getTransaction(def);// 获得事务状态
		try {
			xlMapper.insert2();
			int x = 0;
			int y = 3 / x;
			// 手动提交事务
			transactionManager.commit(status);
		} catch (Exception e) {
			// 手动回滚事务
			transactionManager.rollback(status);
		}
	}

运行项目,调用方法,查看效果:

在这里插入图片描述

进一步测试:注释掉下面两行,效果虽然是一样的,但是,还是写上最好!!

transactionManager.commit(status);
transactionManager.rollback(status);

mi()的完整代码:

/**
	 * 需求:本方法抛出异常时,回滚 抛出异常后,insert2()回滚:不插入
	 */
	private void mi() {
		DefaultTransactionDefinition def = new DefaultTransactionDefinition();
		def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);// 新发起一个事务
		TransactionStatus status = transactionManager.getTransaction(def);// 获得事务状态
		try {
			xlMapper.insert2();
			int x = 0;
			int y = 3 / x;
			// 手动提交事务
//			transactionManager.commit(status);
		} catch (Exception e) {
			// 手动回滚事务
//			transactionManager.rollback(status);
		}
	}

以上说明:真正起作用的是下面3行:

		DefaultTransactionDefinition def = new DefaultTransactionDefinition();
		def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);// 新发起一个事务
		TransactionStatus status = transactionManager.getTransaction(def);// 获得事务状态

而,这3行中又起关键作用的是最后一行 :

TransactionStatus status = transactionManager.getTransaction(def);// 获得事务状态

在这里插入图片描述

通过第2句可知:最后一句是创建一个新事务!而这个新事务会自动完成提交和回滚,所以注释掉 提交和回滚的代码效果是一样的!!特别注意:

  • @Transactional(rollbackFor = Exception.class) 方式创建的事务,如果将异常catch后,在catch块中不再抛出异常,是不会触发回滚的!
  • 但是,在方法中显示手动创建一个事务:
		DefaultTransactionDefinition def = new DefaultTransactionDefinition();
		def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);// 新发起一个事务
		TransactionStatus status = transactionManager.getTransaction(def);// 获得事务状态

这种手动方式创建的事务,在catch块中可以不用再抛出异常,也可以不用显示的写出:transactionManager.rollback(status); ,会自动进行回滚!

在方法中手动创建事务时,transactionManager.rollback(status); 最多只能执行一次,如果方法中有2个及以上地方调用的transactionManager.rollback(status); 回滚方法,那么程序就会抛出异常!如下:

在这里插入图片描述

在这里插入图片描述

在方法中如果执行了事务提交:transactionManager.commit(status); 后面再去执行事务回滚transactionManager.rollback(status);,也会报上图的错误 “事务已完成——不要再同一个事务中提交或回滚超过一次”

也就是说,不管是提交事务,还是回滚事务,二者只能有一个执行并且只能执行一次!!!

在这里插入图片描述

Spring的7中事务传播行为

Propagation.REQUIRED代表当前方法支持当前的事务,且与调用者处于同一事务上下文中,回滚统一回滚(如果当前方法是被其他方法调用的时候,且调用者本身即有事务),如果没有事务,则自己新建事务,
Propagation.SUPPORTS代表当前方法支持当前的事务,且与调用者处于同一事务上下文中,回滚统一回滚(如果当前方法是被其他方法调用的时候,且调用者本身即有事务),如果没有事务,则该方法在非事务的上下文中执行
Propagation.MANDATORY代表当前方法支持当前的事务,且与调用者处于同一事务上下文中,回滚统一回滚(如果当前方法是被其他方法调用的时候,且调用者本身即有事务),如果没有事务,则抛出异常
Propagation.REQUIRES_NEW创建一个新的事务上下文,如果当前方法的调用者已经有了事务,则挂起调用者的事务,这两个事务不处于同一上下文,如果各自发生异常,各自回滚
Propagation.NOT_SUPPORTED该方法以非事务的状态执行,如果调用该方法的调用者有事务则先挂起调用者的事务
Propagation.NEVER该方法以非事务的状态执行,如果调用者存在事务,则抛出异常
Propagation.NESTED如果当前上下文中存在事务,则以嵌套事务执行该方法,也就说,这部分方法是外部方法的一部分,调用者回滚,则该方法回滚,但如果该方法自己发生异常,则自己回滚,不会影响外部事务,如果不存在事务,则与PROPAGATION_REQUIRED一样

在这里插入图片描述

到此这篇关于springboot中手动提交事务的实现方法的文章就介绍到这了,更多相关springboot 手动提交事务内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家! 

相关文章

  • Spring详解四种加载配置项的方法

    Spring详解四种加载配置项的方法

    这篇文章主要给大家介绍了关于springboot加载配置项的四种方式,文中通过示例代码介绍的非常详细,对大家学习或者使用springboot具有一定的参考学习价值,需要的朋友可以参考下
    2022-06-06
  • 简单易懂的Java Map数据添加指南

    简单易懂的Java Map数据添加指南

    Java提供了多种方法来往Map中添加数据,开发者可以根据具体需求选择合适的方法,需要的朋友可以参考下
    2023-11-11
  • Spring中Bean注入源码示例解析

    Spring中Bean注入源码示例解析

    这篇文章主要为大家介绍了Spring中Bean注入源码示例解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-01-01
  • java编程约瑟夫问题实例分析

    java编程约瑟夫问题实例分析

    这篇文章主要介绍了java编程约瑟夫问题实例分析,具有一定借鉴价值,需要的朋友可以参考下。
    2017-12-12
  • java使用@Transactional时常犯的N种错误

    java使用@Transactional时常犯的N种错误

    @Transactional是我们在用Spring时候几乎逃不掉的一个注解,本文主要介绍了使用 @Transactional 时常犯的N种错误,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-01-01
  • java生成jar包并且单进程运行的实例

    java生成jar包并且单进程运行的实例

    下面小编就为大家分享一篇java生成jar包并且单进程运行的实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2017-12-12
  • MyBatisPlus自定义JsonTypeHandler实现自动转化JSON问题

    MyBatisPlus自定义JsonTypeHandler实现自动转化JSON问题

    这篇文章主要介绍了MyBatisPlus自定义JsonTypeHandler实现自动转化JSON问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-12-12
  • 浅谈Spring boot cache使用和原理

    浅谈Spring boot cache使用和原理

    这篇文章主要介绍了浅谈Spring boot cache使用和原理,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-09-09
  • Java中String、StringBuffer和StringBuilder的区别与使用场景

    Java中String、StringBuffer和StringBuilder的区别与使用场景

    在Java编程中,String、StringBuffer和StringBuilder是用于处理字符串的常见类,它们在可变性、线程安全性和性能方面有所不同,具有一定的参考价值,感兴趣的可以了解一下
    2024-05-05
  • Java 使用openoffice进行word转换为pdf的方法步骤

    Java 使用openoffice进行word转换为pdf的方法步骤

    这篇文章主要介绍了Java 使用openoffice进行word转换为pdf的方法步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-04-04

最新评论