@Transactional注解自调用失效问题详解

问题描述

有时候我们在接口方法中配置了@Transactional的注解,但实际使用时却遇到@Transactional注解失效的问题,我们在这里选取一个比较隐秘的细节问题来剖析失效问题。

问题原因分析

注解@Transactional的底层实现是Spring AOP技术,而Spring AOP技术使用的是动态代理,这就意味着对于静态(static)方法和非public方法,注解@Transactional是失效的。还有一个更为隐秘的,而且在使用过程中极其容易犯错的——自调用
所谓自调用就是一个类的一个方法去调用自身另一个方法的过程。
示例代码:

@Service
public class RoleServiceImpl extends RoleService{
	
	@Autowired
	private RoleMapper roleMapper=null;
	
	@Override
	@Transactional(propagation = Propagation.REQUIRES_NEW,isolation = Isolation.READ_COMMITTED)
	public int insertRole(Role role) {
		
		return roleMapper.insertRole(role);
	}
	
	@Override
	@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.READ_COMMITTED)
	public int insertRoleList(List<Role> roleList) {
		int count=0;
		for (Role role:roleList) {
			try {
				insertRole(role);
				count++;
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return count;
	}
	
}

在insertRoleList的方法实现中,它调用了自身类的实现insertRole方法,而insertRole方法声明是REQUIRES_NEW的传播行为,也就是每次调用该方法就会产生新的事务运行,但是实际结果却是每次插入都是用了相同的事务。也就是说在insertRole上标注的@Transcational失效了。
出现这个问题的根本原因在于AOP的实现原理。由于@Transcational的实现原理是AOP,而AOP的实现原理是动态代理,而在上述代码中使用的是自己调用自己的过程,也就是说根本不存在代理对象的调用,这样就不会产生AOP为我们设置@Transactional配置的参数,这样就出现了自调用注解失效的问题。

解决方法

为了解决这个问题可以使用两个服务类进行插入的调用,这样可以使SpringIoc容器生成代理对象,解决自调用的问题。
另一种方法是可以直接从容器中获取RoleService的代理对象,解决自调用问题。
示例代码:

@Override
@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.READ_COMMITTED)
public int insertRoleList(List<Role> roleList) {
	int count=0;
	//从容器中获取RoleService对象,实际是一个代理对象
	RoleService service = ctx.getBean(RoleService.class);
	for (Role role:roleList) {
		try {
			service.insertRole(role);
			count++;
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	return count;
}

从容器中获取代理对象的方法客服了自调用的过程,但是有一个弊端,就是从容器中获取对象的方法有侵入之嫌,你的类需要依赖于SpringIOC容器。所以我们推荐使用另一个服务类调用解决自调用问题。

你可能感兴趣的:(java踩坑系列)