最近工作过程中,涉及到本地事务这块的代码总是不能按照自己的思路执行和回滚,所以抽时间静下心来亲自实验一下并且整理成文档供大家参考:
对于事务的学习有个小小提议,例如我以前看了好多事务相关的文档,自己以为弄懂了,可以真正开发的过程中,还是不行。所以希望大家没事的时候,可以亲自实验一下,这个比你看100变文章都要理解深刻的多。
一、事务生效:
现象:报错之后,方法一和方法二插入数据库的数据都会回滚。
原因:事务的传播属性,方法二复用方法一的事务,所以一旦报错,全部回滚,数据库没有任何数据。
/**
* 注解 Transactional 声名式事务
* @return
*/
@Transactional(rollbackFor = Exception.class)
public RestResponse doTransactional() {
OrderSupplier orderSupplier = new OrderSupplier();
orderSupplier.setOrderSupplierNum("123");
//事务一
orderSupplierExMapper.insertSelective(orderSupplier);
saveCustomer();
return RestResponse.success(true);
}
public void saveCustomer(){
OrderCustomer record = new OrderCustomer();
record.setOrderNum("123");
//事务二
orderCustomerExMapper.insertSelective(record);
//制造错误,看看能不能回滚
int result = 1/0;
}
日志信息:通过日志信息可以看到方法一和方法二都是用的同一事务
org.apache.ibatis.session.defaults.DefaultSqlSession@18b97c27,并且第二红框可以看到是方法二获取(Fetched)的方法一的事务。
二、事务不生效
现象:报错之后,方法一和方法二都会插入数据库,不会回滚。
原因:方法一不存在事务,方法二虽然有声明事务,但是service之间的方法调用,Spring中对于这样的方法没有实现AOP代理拦截,没有实现事务。
/**
* 本方法没有事务,调用方法添加事务
* @return
*/
public RestResponse doTransactional() {
OrderSupplier orderSupplier = new OrderSupplier();
orderSupplier.setOrderSupplierNum("123");
orderSupplierExMapper.insertSelective(orderSupplier);
saveCustomer();
return RestResponse.success(true);
}
/**
* 注解 Transactional 开始声名式事务
* @return
*/
@Transactional(rollbackFor = Exception.class)
public void saveCustomer(){
OrderCustomer record = new OrderCustomer();
record.setOrderNum("123");
//事务二
orderCustomerExMapper.insertSelective(record);
//制造错误,看看能不能回滚
int result = 1/0;
}
对应的日志信息:可以看到整个流程没有生成相关的事务。
三、事务生效
现象:程序报错,方法一成功插入数据库,方法二数据会进行回滚。
原因:方法二在内外的service中,Spring中对于这样的方法实现AOP代理拦截,实现了事务。
public class OrderSupplierService {
@Autowired
private OrderCustomerExMapper orderCustomerExMapper;
public RestResponse doTransactional() {
OrderSupplier orderSupplier = new OrderSupplier();
orderSupplier.setOrderSupplierNum("123");
// saveCustomer失败,不会进行回滚
orderSupplierExMapper.insertSelective(orderSupplier);
customerService.saveCustomer();
return RestResponse.success(true);
}
}
public class CustomerService {
@Autowired
private OrderCustomerExMapper orderCustomerExMapper;
/**
* 注解 Transactional 开始声名式事务
* @return
*/
@Transactional(rollbackFor = Exception.class)
public void saveCustomer(){
OrderCustomer record = new OrderCustomer();
record.setOrderNum("123");
//失败 会进行回滚
orderCustomerExMapper.insertSelective(record);
int result = 1/0;
}
}
日志信息:从日志可以看出第二个方法生成了事务。
四、大事务解决思路
问题:在日常工作中,经常会存在业务逻辑比较复杂,操作数据库的地方很多,但是也有很多代码是不涉及到数据库或者只涉及到数据库查询。
做法:我们往往是为了达到一旦出错回滚操作数据的目的,会在整个方法上加上事务注解。
结果:导致事务长时间释放不了。最后导致数据库连接增高。
解决方法:我们可以将查询的数据或者不涉及到数据库的操作的代码单独放在一个方法里面,然后新增加一个对应的操作数据的类来操作数据,并且加事务注解。
public class DemandOrderDispatchService{
@Autowired
private DispatchOrderDbService dispatchOrderDbService;
public void dispatch(DemandOrderDispatchDTO demandOrderDispatch) {
// 获取 customerOrderList操作
// 获取 supplierVehicleInfoList操作
dispatchOrderDbService.dispatchOrderSave(customerOrderList,supplierVehicleInfoList);
}
}
@Transactional(rollbackFor = Exception.class)
public void dispatchOrderSave(final List<OrderCustomer> customerOrderList, final List<DemandOrderVehicleSupplier> supplierVehicleInfoList,
final DemandOrder demandOrderForUpdate) {
supplierVehicleInfoList.forEach(demandOrderVehicleSupplierMapper::insertSelective);
dispatchOrderRecords.forEach(dispatchOrderRecordMapper::insertSelective);
orderSupplierService.saveOrderSupplier(orderSupplierList);
}
五、 事务成功提之后需要做某些处理
在我们的工作过程中,可能有些业务,是处理异步的处理流程,那么就需要我们在成功提交数据之后,才能根据数据库里面的数据做某些操作。比如:我们需要在事务成功提交之后,才能发送某个MQ信息到队列。不然可能出现发送mq成功,但是数据库回滚的情况。可以用TransactionSynchronizationManager.registerSynchronization处理。
@Transactional(rollbackFor = Exception.class)
public RestResponse doTransactional() {
OrderSupplier orderSupplier = new OrderSupplier();
orderSupplier.setOrderSupplierNum("123");
orderSupplierExMapper.insertSelective(orderSupplier);
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
@Override
public void afterCommit() {
try {
// 等待事务提交成功之后需要执行的代码
} catch (Exception e) {
log.error("afterCommitAsync exception", e);
}
}
});
return RestResponse.success(true);
}
六、最后需要,如果对于@Transactional事务注解不好理解,可以使用手动事务。Spring就提供了手动事务。网上关于这样有好多代码,可以自己学习一下。
总之,事务是Java一块能把人搞晕的知识点,所以一定自己手动试一下。