spring使用Aop事务失效问题
问题:项目中碰到在同一个类中(JDK动态代理),非事务方法调用一个有事务方法,会使得整个事务失效
问题重现
利用mysql中ReplicationDriver
的特性:如果加了@transaction
注解就从主库取数据,而加@transaction(readOnly=true)
时则到从库取数据的原理
现主库有数据:
{
"id": 1,
"userName": "test_test",
"password": "test_replica",
"createTime": 1534591529000,
"modifyTime": 1534591531000,
"isDelete": 1
}
从库有数据:
{
"id": 1,
"userName": "test_test",
"password": "test_replica",
"createTime": 1534591529000,
"modifyTime": 1534591531000,
"isDelete": 0
}
UserService.java
:
public interface UserService {
public void selectUserFromMaster() throws JsonProcessingException;
public void selectUsers() throws JsonProcessingException;
}
实现类UserServiceImpl.java
:
@Service("userService")
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
@Transactional
public void selectUserFromMaster() throws JsonProcessingException {
// TransactionDebugUtils.transactionRequired("UserService.selectUserFromMaster");
UserDo userDo = userDao.selectByPrimaryKey(1L);
ObjectMapper mapper = new ObjectMapper();
System.out.println(mapper.writeValueAsString(userDo));
}
public void selectUsers() throws JsonProcessingException {
selectUserFromMaster();
}
}
申明全局事务,所有以select
为前缀都从库取数据:
<tx:annotation-driven transaction-manager="transactionManager"/>
<tx:advice id="txAdvice">
<tx:attributes>
<tx:method name="*" propagation="REQUIRED"/>
<tx:method name="select*" read-only="true"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="pointCut"
expression="execution(* com.**.persistent.dao.*.*(..))"></aop:pointcut>
<aop:advisor advice-ref="txAdvice" pointcut-ref="pointCut"/>
</aop:config>
测试案例:
public class UserServiceTest extends BaseTest {
@Autowired
private UserService userService;
@Test
public void selectUserFromMasterTest() throws JsonProcessingException {
userService.selectUserFromMaster();
}
@Test
public void selectUsersTest() throws JsonProcessingException {
userService.selectUsers();
}
}
测试结果:
[selectUserFromMasterTest](主库数据):
{
"id": 1,
"userName": "test_test",
"password": "test_replica",
"createTime": 1534591529000,
"modifyTime": 1534591531000,
"isDelete": 1
}
[selectUsersTest](从库):
{
"id": 1,
"userName": "test_test",
"password": "test_replica",
"createTime": 1534591529000,
"modifyTime": 1534591531000,
"isDelete": 0
}
解决方案
首先在xml中加入:
<aop:aspectj-autoproxy expose-proxy="true"/>
然后修改UserServiceImpl.java文件
:
@Service("userService")
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
@Transactional
public void selectUserFromMaster() throws JsonProcessingException {
TransactionDebugUtils.transactionRequired("UserService.selectUserFromMaster");
UserDo userDo = userDao.selectByPrimaryKey(1L);
ObjectMapper mapper = new ObjectMapper();
System.out.println(mapper.writeValueAsString(userDo));
}
public void selectUsers() throws JsonProcessingException {
((UserService)AopContext.currentProxy()).selectUserFromMaster();
}
}
运行测试案例selectUsersTest
结果:
[selectUsersTest](主库):
{
"id": 1,
"userName": "test_test",
"password": "test_replica",
"createTime": 1534591529000,
"modifyTime": 1534591531000,
"isDelete": 1
}
经测试,如果同样是类代理模式中(CGLIB动态代理),在这样的一样环境,事务也还是会失效,解决方法和这个一样
一个测试获取事务的工具类文章: