【手动事务】SpringMVC手动提交事务(声明式事务)同时对多个表插入数据,异常全部回滚

之前关于Spring事务只是简单的了解,直接在项目的applicationContext里面直接配置了事务管理之后,就不用关心事务的提交了,spring会帮我们自动提交事务,在异常时直接抛出运行时异常了。

但是有时候在项目中需要一系列连续的操作,比如对多个表同时执行插入新数据的操作,在其中一个出现异常时,就全部回滚,这时原配置文件里的的自动提交事务就不能符合需求了。

但是手动提交事务又不懂,又开始了查资料,在查了大半天资料之后,对spring的事务管理了解了点皮毛,这里记录一下。

刚开始在网上搜手动提交事务时,发现好多重复的文章,只是简单的将代码贴出来了,没有必要的说明,对于我这种小白来说,理解不了,也不知道怎么运用到项目中去,这里将我自己的爬坑历程记录一下,只是简单的实现了,手动控制事务提交,异常自动回滚,但是我直接将事务管理放在了controller的方法里面,但是在翻了很多博客之后,发现很多人说事务要放在service层里,但是放在service层里面,如果我使用两个service都进行插入数据的操作,这样就只有一个回滚,另一个还是自动提交了,没有实现需求中的异常时本次操作全部回滚的目的。

【手动事务】SpringMVC手动提交事务(声明式事务)同时对多个表插入数据,异常全部回滚_第1张图片

在刚开始时,我测试了一下自动提交事务,直接在其中一个service里面加入一个异常

	public int insertSelective(User user) {
		
		int i = userDao.insertSelective(user);
		System.out.println("1111111");
		int a = 10/0;	// 抛出异常
		int i2 = userDao.insertSelective(user);
		if (i>0 && i2 > 0) {
			return 1;
		}
		return 0; 
	}

然后在controller里面执行两次插入操作:

		try {
			insertSelective2 = roleService.insertSelective(role);
			insertSelective = userService.insertSelective(user);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			// e.printStackTrace();
			System.out.println("出现异常");
		}

在debug时发现,控制台可以正常打印1111,但是数据库里并没有新的用户信息,可见虽然执行了第一句插入用户的操作,但是事务还没有提交,插入数据的动作还没完成,这时遇到了除数是0的异常,事务就直接回滚了,但是controller类里的insertSelective2 = roleService.insertSelective(role);这一句仍然正常执行了,并且产生了新的数据。到这里我才发现好像事务是在service的基础上分开的,每个service有一个自己的事务,事务异常了,不会影响到别的service的业务。

后来又查资料之后,发现自己在applicationContext的配置文件里面,配置了事务的切面了,就是以service为切面配置的,这样每个service就会有一个独立的事务,事务的提交不会影响到别的已完成的事务。
配置如下:

	
	<aop:config>
		<aop:pointcut id="txPoint" expression="execution(* com.yang.service.*.*(..))" />
		<aop:advisor advice-ref="txAdvice" pointcut-ref="txPoint" />
	aop:config>

这里可以看到,事务是建立在service的层次上的,所以才会在执行插入角色(role)信息之后不会因为插入用户信息(user)中间出现异常而回滚。

不知道我这样理解的对不对,暂时先这么理解了,貌似也说的通。

然后我又翻到博客里直接说道声明式事务的写法,我对比我的测试项目里的配置文件,尝试在controller类里试了一下,将两次插入操作直接写在手动开启事务和提交事务之间,结果却是正常回滚,后面的发生异常时,两次插入操作都回滚了,实现了上面所说的功能需求。
applicationContext.xml配置文件中关于事务的部分为:

	
	<bean id="transactionManager"
		class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource" />
	bean>
	
	
	<tx:advice id="txAdvice" transaction-manager="transactionManager">
		
		<tx:attributes>
			<tx:method name="insert*" propagation="REQUIRED" />
			<tx:method name="update*" propagation="REQUIRED" />
			<tx:method name="edit*" propagation="REQUIRED" />
			<tx:method name="save*" propagation="REQUIRED" />
			<tx:method name="add*" propagation="REQUIRED" />
			<tx:method name="new*" propagation="REQUIRED" />
			<tx:method name="set*" propagation="REQUIRED" />
			<tx:method name="remove*" propagation="REQUIRED" />
			<tx:method name="delete*" propagation="REQUIRED" />
			<tx:method name="change*" propagation="REQUIRED" />
			<tx:method name="check*" propagation="REQUIRED" />
			<tx:method name="get*" propagation="REQUIRED" read-only="true" />
			<tx:method name="find*" propagation="REQUIRED" read-only="true" />
			<tx:method name="load*" propagation="REQUIRED" read-only="true" />
			<tx:method name="*" propagation="REQUIRED" read-only="true" />
		tx:attributes>
	tx:advice>

	
	<aop:config>
		<aop:pointcut id="txPoint" expression="execution(* com.yang.service.*.*(..))" />
		<aop:advisor advice-ref="txAdvice" pointcut-ref="txPoint" />
	aop:config>

controller类里的手动创建事务和手动提交事务内容:

	// 首先在controller类里注入事务管理器,name的值为配置文件里的事务管理器的名称
	@Resource(name = "transactionManager")
	private DataSourceTransactionManager transactionManager;

	// 测试方法
	@RequestMapping("/test")
	@ResponseBody
	public String test2() {
		DefaultTransactionDefinition transDefinition = new DefaultTransactionDefinition();
		// 开启新事物
		transDefinition.setPropagationBehavior(DefaultTransactionDefinition.PROPAGATION_REQUIRES_NEW);// 事物隔离级别,开启新事务
		TransactionStatus transStatus = transactionManager.getTransaction(transDefinition);// 获得事务状态
		User user = new User();
		user.setUsername("xiaoming");
		user.setPassword("111");
		user.setRoleId(3);

		TRole role = new TRole();
		role.setRolename("test");
		int insertSelective = 0;
		int insertSelective2 = 0;
		try {
			insertSelective2 = roleService.insertSelective(role);
			insertSelective = userService.insertSelective(user);
			transactionManager.commit(transStatus); 	// 提交事务
			System.out.println("result:" + insertSelective);
			System.out.println("result:" + insertSelective2);
		} catch (Exception e) {
			transactionManager.rollback(transStatus);	// 异常回滚
			return "false";
		}
		return "success";
	}

可以看到这里将事务的提交和回滚清楚的列出来了,这里手动创建开启事务,然后执行插入操作,如果某一个插入操作出现了异常,直接进入catch里面,执行异常回滚了,这样就可以实现,这些不同表里插入数据,一旦某一个插入出现了异常,都会事务都不会提交的问题了。

到这里问题好像解决了,实现了对多个表操作,异常全部回滚的需求了。但是我看到有博客说事务最好是写在service层里,controller里面只处理请求的转发不进行业务的处理,但是没有找到更好的解决办法,也只能先这样写了。

你可能感兴趣的:(【mybatis】,【Spring】,【总结】)