在java中事务的使用在多表关联操作的业务处理中必不可少的。
注解式事务是使用最简单方便的,也是比较推荐的方式。下面介绍在SpringBoot中注解式事务的使用示例。
1.添加事务注解和事务的使用范围
以下是最简单是事务代码示例,其中taDAO和tbDAO是DAO层方法
@Transactional
public boolean updateInfo(HashMap<String, Object> map) {
boolean flag=false;
try {
//update表A
taDAO.update("1","小王");
//update表B
tbDAO.update("22","技术部");
flag=true;
} catch (Exception e) {
logger.error(estr + e.toString());
throw new RuntimeException();// 抛出RuntimeException异常给事务处理回滚
}
return flag;
}
问题:事务注解应该加在哪个层?
在需要的方法和类上添加注解@Transactional
上就可以。注意:添加在类上时,说明此类的public
的方法都行事务管理。建议只在需要的方法上添加事务注解@Transactional
。
事务注解应添加在实现业务逻辑的Serviceimpl层,不要加到controller层或Service接口层。因为Serviceimpl层是具体业务的实现类,里面会调用到底层DAO的数据库
操作(事务提交和回滚的作用对象就是数据库操作),而controller层只是负责调用Service接口,Serviceimpl实现Service接口。所以在Serviceimpl层设置事务是最合适的。
2.事务提交与回滚
上面的代码中,添加完事务注解@Transactiona
后,方法add就启用了事务管理。接下来用这个示例对事务的提交和回滚做一下说明。
以下是我测试的过程中验证以上这段代码能否自动回滚(读者也可以自己同步测试验证一下),
其中会结合网上的用法做补充说明(如:事务强调只能捕获RuntimeException异常和设置手动回滚TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()的问题)。
1) 问题:看过网上介绍的很多事务代码段里,都是调用多个DAO方法操作数据表,但如果调用了其他Serviceimpl方法,事务是否能回滚?
如果使用Mybaits持久化框架,简单来说DAO层就是映射mybatis中Mapperxml文件的接口,调用对应接口可以直接执行SQL语句,没有对入参做验证操作,而Serviceimpl方法封装了必要的验证过程,减少了外部调用DAO的重复验证工作量。
情况一调用DAO方法:updat 表A、updat 表B是修改数据表的DAO方法,执行updat 表B时SQL报错异常时,则整个updateInfo方法回滚,之前执行成功的update表A也被撤回到原值。
情况二调用Serviceimpl方法:若updat 表A是修改数据表的DAO方法,而updat 表B是Serviceimpl层方法,该方法内部调用了DAO方法。
@Transactional
public boolean add(HashMap<String, Object> map) {
try {
//update表A
taDAO.update("1","小王");
//update表B
tbServImpl.update("22","技术部");
} catch (Exception e) {
logger.error(estr + e.toString());
throw new RuntimeException(); //抛出RuntimeException异常给事务处理回滚
}
return true;
}
tbServImpl部分代码如下:
public boolean update(String id,String name) {
boolean flag=false;
try {
//update表B
tbDAO.update(id,name);
flag=true;
} catch (Exception e) {
logger.error(estr + e.toString());
throw new RuntimeException(); //抛出RuntimeException异常给事务处理回滚
}
return flag;
}
此时,若tbServImpl.update执行SQL报错,updateInfo事务同样会回滚,说明一个事务代码段中,不一定限定只能执行DAO底层才能回滚,执行Serviceimpl层方法也同样适用。
2) 问题:什么样的异常会回滚?
上述代码中,SQL语句错误就会抛出异常被try catch捕获到,按正常网上的说法,事务只能捕获RuntimeException异常,所以这里的Exception e还不行,需要在catch中再抛出RuntimeException异常才能正常回滚。
3) 问题:如何手动回滚?
异常回滚是执行过程中抛出异常给事务去回滚,但有时执行没有异常报错,却执行失败需要手动回滚。比如update表B时传入的id有误,更新条数是0,此时需要手动回滚update表A的操作。手动回滚有两种方法可以实现
(1)判断执行结果有误时,手动new 一个异常抛出
@Transactional
public boolean add(HashMap<String, Object> map) {
try {
//update表A
taDAO.update("1","小王");
//update表B
if(!tbServImpl.update("22","技术部")){
//设置手动回滚 方法1
throw new Exception();//手动抛异常给事务回滚
//设置手动回滚 方法2
//TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()
}
} catch (Exception e) {
logger.error(estr + e.toString());
throw new RuntimeException(); //抛出RuntimeException异常给事务处理回滚
}
return true;
}
(2)设置手动回滚TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(),代码同上。
(3)回滚点
使用Object savePoint = TransactionAspectSupport.currentTransactionStatus().createSavepoint(); 设置回滚点,
使用TransactionAspectSupport.currentTransactionStatus().rollbackToSavepoint(savePoint);回滚到savePoint。