我前面有一个文章讲了当调用本类方法时,被调用方法的@Transactional注解会失效,所以建议大家用编程式事务。
Spring/SpringBoot实现编程式事务
然而今天,自己推翻我自己,分享一个怎么在调用当前类带有事务注解的方法时,还能强制使事务生效的方式。
((YourClass) AopContext.currentProxy()).withTransactionMethod(keyword);
这种写法相当于用AOP的方式调用同类的方法,使得@Transactional注解生效。spring boot注解记得要加上
@EnableAspectJAutoProxy(exposeProxy = true)来暴露AOP的Proxy对象才行,否则会报异常。
写个例子体会一下:
private final Logger logger = LoggerFactory.getLogger(AopService.class);
@Autowired
private UserMapper userMapper;
public int insertBatchData(List<User> userList) {
int count = 0;
for (User user : userList) {
try {
// 使用AOP的方式调用本类方法
count += ((AopService) AopContext.currentProxy()).insertOneData(user);
} catch (Exception e) {
// 发生异常只记录日志不阻断程序
logger.error("数据插入异常", e);
}
}
return count;
}
@Transactional(rollbackFor = Exception.class)
public int insertOneData(User user) {
int count = userMapper.insert(user);
if ("李四".equals(user.getName())) {
// 强行抛出异常测试事务回滚
throw new RuntimeException("禁止李四加入!");
}
return count;
}
UserMapper就是数据库操作类,写一个简单地插入方法就好,来这里的都是成熟的开发了,不再展示源码了。
再写一个测试方法测试一下这段代码:
@Autowired
private AopService aopService;
@Test
public void testInsertBatch() {
int result = aopService.insertBatchData(Arrays.asList(creatUser("张三"), creatUser("李四"), creatUser("王五")));
System.out.println("数据库更新的条目数为 "+result);
}
// 生成实体类对象
private User creatUser(String name) {
User user = new User();
user.setName(name);
user.setAge((int) (Math.random() * 80));
user.setEmail("[email protected]");
return user;
}
注意,以上测试代码使用了Spring的注解,需要启动Spring容器才能执行,在测试类上加入以下注解以启动Spring容器:
@SpringBootTest(classes = SpringBatchApplication.class)
@RunWith(SpringRunner.class)
SpringBatchApplication.class是我的SpringBoot工程启动类,注意替换
运行后结果为:
2021-11-28 17:51:56.205 ERROR 9984 --- [ main] c.term.spring_batch.service.AopService : 数据插入异常
java.lang.IllegalStateException: Cannot find current proxy: Set 'exposeProxy' property on Advised to 'true' to make it available, and ensure that AopContext.currentProxy() is invoked in the same thread as the AOP invocation context.
at org.springframework.aop.framework.AopContext.currentProxy(AopContext.java:69) ~[spring-aop-5.3.8.jar:5.3.8]
at com.term.spring_batch.service.AopService.insertBatchData(AopService.java:26) ~[classes/:na]
这个报错就是因为我没有设置exposeProxy = true,解决方法:
在启动类上加注解:@EnableAspectJAutoProxy(exposeProxy = true)
不要忘记导入Spring AOP的jar包嗷
加完注解再次测试试一下,结果如下:
2021-11-28 17:57:02.488 DEBUG 2836 --- [ main] c.t.s.mapper.UserMapper.insert : ==> Preparing: INSERT INTO user ( name, age, email ) VALUES ( ?, ?, ? )
2021-11-28 17:57:02.494 DEBUG 2836 --- [ main] c.t.s.mapper.UserMapper.insert : ==> Parameters: 张三(String), 26(Integer), shabi@111.com(String)
2021-11-28 17:57:02.496 DEBUG 2836 --- [ main] c.t.s.mapper.UserMapper.insert : <== Updates: 1
2021-11-28 17:57:02.501 DEBUG 2836 --- [ main] c.t.s.mapper.UserMapper.insert : ==> Preparing: INSERT INTO user ( name, age, email ) VALUES ( ?, ?, ? )
2021-11-28 17:57:02.501 DEBUG 2836 --- [ main] c.t.s.mapper.UserMapper.insert : ==> Parameters: 李四(String), 25(Integer), shabi@111.com(String)
2021-11-28 17:57:02.502 DEBUG 2836 --- [ main] c.t.s.mapper.UserMapper.insert : <== Updates: 1
2021-11-28 17:57:02.510 ERROR 2836 --- [ main] c.term.spring_batch.service.AopService : 数据插入异常
java.lang.RuntimeException: 禁止李四加入!
at com.xxxx.spring_batch.service.AopService.insertOneData(AopService.java:40) ~[classes/:na]
(报错堆栈省略,反正是我自己抛的)
2021-11-28 17:57:02.511 DEBUG 2836 --- [ main] c.t.s.mapper.UserMapper.insert : ==> Preparing: INSERT INTO user ( name, age, email ) VALUES ( ?, ?, ? )
2021-11-28 17:57:02.511 DEBUG 2836 --- [ main] c.t.s.mapper.UserMapper.insert : ==> Parameters: 王五(String), 33(Integer), shabi@111.com(String)
2021-11-28 17:57:02.512 DEBUG 2836 --- [ main] c.t.s.mapper.UserMapper.insert : <== Updates: 1
数据库更新的条目数为 2
可以看到在插入李四之后抛了异常,如果事务生效,那么李四那条数据将会被回滚掉,去数据库确认一下:
如上,@Transactional事务在同类中生效