Spring事务依赖的是数据库的事务,在开发中如果没有处理好,可能会遇到事务失效的情况,本文就盘点Spring事务会失效的一些情况并给出解决方案。
Spring事务分为声明式事务和编程时事务两种。看下声明式事务。
使用@Transactional做声明式事务的一些说明
- @Transactional等价与@Transactional(propagation=Propagation.REQUIED),如果当前线程中存在事务,则使用该事务执行,如果不存在,就新创建一个事务。
- @Transactional(propagation=Propagation.REQUIED_NEW),不管当前线程是否存在事务,都会新建一个新的事务继续执行,执行完毕之后,唤醒之前挂起的事务,继续执行。
使用@Transactional做声明式事务的流程
- 创建事务(getTransaction)
- 获取数据库连接
- 提交事务(commit)
- 回滚事务(rollback)
- 释放数据库连接
通过几个实例来解释事务失效的问题
- 情况1:
要求:执行child()是开启一个新的事务。
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
/**
* DESCRIPTION:
* * @author liu.qq
* @create 2019-09-29 14:16
**/
@Service
@Slf4j
public class TestService {
@Autowired
private OrderMapper orderMapper;
@Transactional
public void parent() {
try {
//child被this调用
child();
} catch (Exception e) {
log.info("出现异常:");
}
Order order = new Order();
order.setOrderNo("parent");
order.setTitle("parent");
order.setStatus(0);
order.setAmount(100);
orderMapper.insert(order);
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void child() {
Order order = new Order();
order.setOrderNo("parent");
order.setTitle("parent");
order.setStatus(0);
order.setAmount(100);
orderMapper.insert(order);
int a = 1 / 0;
}
}
执行结果:child()执行成功,parent()执行成功,child()事务失效的根本原因是动态代理导致的,只有被代理对象直接调用的方法才能被代理。这里是TestService 对象直接调用的,而不是其代理对象。所以child()方法的执行就相当于普通的方法的执行。
解决方案1:
从AOP上下文获取本类的代理对象,使用代理对象执行child()。
@Transactional
public void parent() {
try {
// child();
TestService testService = (TestService)AopContext.currentProxy();
testService.child();
} catch (Exception e) {
log.info("出现异常:");
}
Order order = new Order();
order.setOrderNo("parent");
order.setTitle("parent");
order.setStatus(0);
order.setAmount(100);
orderMapper.insert(order);
}
执行结果:child()失败,parent()成功。
解决方案2:
从Spring容器获取被代理类的代理对象,使用代理对象执行child()方法。
package cn.arbexpress.court.transactional;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
/**
* DESCRIPTION:
*
* @author liu.qq
* @create 2019-09-29 14:16
**/
@Service
@Slf4j
public class TestService implements ApplicationContextAware {
@Autowired
private OrderMapper orderMapper;
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
@Transactional
public void parent() {
try {
TestService bean = applicationContext.getBean(TestService.class);
bean.child();
} catch (Exception e) {
log.info("出现异常:");
}
Order order = new Order();
order.setOrderNo("parent");
order.setTitle("parent");
order.setStatus(0);
order.setAmount(100);
orderMapper.insert(order);
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void child() {
Order order = new Order();
order.setOrderNo("parent");
order.setTitle("parent");
order.setStatus(0);
order.setAmount(100);
orderMapper.insert(order);
int a = 1 / 0;
}
}