背景
- 推荐阅读Spring的event的基本使用过程与理解
- 测试当线程池中的任务发生异常(RuntimeException)不捕获与@Transactional关系
过程
- 测试事务(入口处没有注解@Transactional)
- 测试逻辑。业务逻辑代码先执行了数据库的更新操作,后续逻辑发生空指针异常。
- 数据库情况
- 代码情况
监听代码情况
@EventListener
@Async("AsyncTaskExecutor")
public void handleMyEvent(MyEvent myEvent) {
System.out.println("线程:" + Thread.currentThread().getName() + "执行了MyEvent事件");
// 具体的业务逻辑过程
this.eventService.handleEvent(myEvent.getAddr(), myEvent.getTime(), myEvent.getOperator());
}
具体业务逻辑代码。
studentService.selectByName()和studentService.update()都添加了@Transactional注解。异常是RuntimeException,因此Spring是能够感知到的。
@Override
public void handleEvent(String addr, Date time, String operator) {
System.out.println("event: 模拟具体的业务逻辑处理过程, 这里单纯地打印出来即可");
System.out.println("myEvent.addr: " + addr);
System.out.println("myEvent.time: " + time);
System.out.println("myEvent.operator: " + operator);
Student sara = studentService.selectByName("sara");
sara.setName("Ted");
studentService.update(sara);
String str = null;
System.out.println(str.equals("null"));
}
- 测试结果(未回滚,数据被修改 了)
- 测试事务(入口处有注解@Transactional),其他为原来初始情况。
监听代码
@EventListener
@Async("AsyncTaskExecutor")
@Transactional
public void handleMyEvent(MyEvent myEvent) {
System.out.println("线程:" + Thread.currentThread().getName() + "执行了MyEvent事件");
// 具体的业务逻辑过程
this.eventService.handleEvent(myEvent.getAddr(), myEvent.getTime(), myEvent.getOperator());
}
测试结果(回滚了,没有修改成功)
- 测试事务(入口处没有注解@Transactional,业务逻辑入口处有@Transactional ),其他为原来初始情况。
@EventListener
@Async("AsyncTaskExecutor")
public void handleMyEvent(MyEvent myEvent) {
System.out.println("线程:" + Thread.currentThread().getName() + "执行了MyEvent事件");
// 具体的业务逻辑过程
this.eventService.handleEvent(myEvent.getAddr(), myEvent.getTime(), myEvent.getOperator());
}
@Override
@Transactional
public void handleEvent(String addr, Date time, String operator) {
System.out.println("event: 模拟具体的业务逻辑处理过程, 这里单纯地打印出来即可");
System.out.println("myEvent.addr: " + addr);
System.out.println("myEvent.time: " + time);
System.out.println("myEvent.operator: " + operator);
Student sara = studentService.selectByName("sara");
System.out.println(sara.getName());
sara.setName("Ted");
studentService.update(sara);
String str = null;
System.out.println(str.equals("null"));
}
测试结果(事务回滚)
- 测试事务(入口处有注解@Transactional,业务逻辑入口处有@Transactional ,studentService.update(sara)@Transactional去掉)。
@EventListener
@Async("AsyncTaskExecutor")
public void handleMyEvent(MyEvent myEvent) {
System.out.println("线程:" + Thread.currentThread().getName() + "执行了MyEvent事件");
// 具体的业务逻辑过程
this.eventService.handleEvent(myEvent.getAddr(), myEvent.getTime(), myEvent.getOperator());
}
@Override
@Transactional
public void handleEvent(String addr, Date time, String operator) {
System.out.println("event: 模拟具体的业务逻辑处理过程, 这里单纯地打印出来即可");
System.out.println("myEvent.addr: " + addr);
System.out.println("myEvent.time: " + time);
System.out.println("myEvent.operator: " + operator);
Student sara = studentService.selectByName("sara");
System.out.println(sara.getName());
sara.setName("Ted");
// 此方法没有@Transactional
studentService.update(sara);
String str = null;
System.out.println(str.equals("null"));
}
测试结果(事务回滚)
小结
- Spring的线程池用在Event中,如果线程池中的线程在处理业务逻辑的过程中,发生了RuntimeExcepion时,与Spring注解@Transactional用在的哪个层级,然后导致事务会回滚,有非常大的关系。这个demo实验有很好的参考价值。
- 注意@Transactional默认回滚的异常级别是RuntimeExcepion,而不是Excption
- @Transactional。Spring默认的传播行为REQUIRED,业务方法需要在一个事务中运行,如果方法运行时,已处在一个事务中,那么就加入该事务,否则自己创建一个新的事务。