之前关于Spring事务只是简单的了解,直接在项目的applicationContext里面直接配置了事务管理之后,就不用关心事务的提交了,spring会帮我们自动提交事务,在异常时直接抛出运行时异常了。
但是有时候在项目中需要一系列连续的操作,比如对多个表同时执行插入新数据的操作,在其中一个出现异常时,就全部回滚,这时原配置文件里的的自动提交事务就不能符合需求了。
但是手动提交事务又不懂,又开始了查资料,在查了大半天资料之后,对spring的事务管理了解了点皮毛,这里记录一下。
刚开始在网上搜手动提交事务时,发现好多重复的文章,只是简单的将代码贴出来了,没有必要的说明,对于我这种小白来说,理解不了,也不知道怎么运用到项目中去,这里将我自己的爬坑历程记录一下,只是简单的实现了,手动控制事务提交,异常自动回滚,但是我直接将事务管理放在了controller的方法里面,但是在翻了很多博客之后,发现很多人说事务要放在service层里,但是放在service层里面,如果我使用两个service都进行插入数据的操作,这样就只有一个回滚,另一个还是自动提交了,没有实现需求中的异常时本次操作全部回滚的目的。
在刚开始时,我测试了一下自动提交事务,直接在其中一个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里面只处理请求的转发不进行业务的处理,但是没有找到更好的解决办法,也只能先这样写了。