解决:同一个类中方法调用,导致@Transactional失效(AopContext.currentProxy())

我前面有一个文章讲了当调用本类方法时,被调用方法的@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事务在同类中生效

猜你喜欢

转载自blog.csdn.net/weixin_41674401/article/details/121594630