记录一次spring事务不生效的情况

是不是spring加上@Transactional注解就可以不用管事务了,有没有考虑事务不生效的情况

前段时间帮老大面试,问到了spring的事务,好几个人都表示加上@Transactional注解就可以了,但是有没有去想为什么可以,有哪些情况下@Transactional注解会失效(有很多情况会失效,我这里只说少量的几种)

事务注解工作原理

首先,事务注解工作原理是啥,程序里没有黑科技,别人能做的你大多数能做,首先想想没有事务注解时人们怎么处理事务代码的,其实很简单,curd开始前begin(),curd处理之后根据结果commit() 或者 rollback(),说到这里,应该能猜到,注解就是在你方法之前加上begin逻辑,然后捕获异常(可配置异常种类),查询异常是否在配置的回滚异常里面,如果在配置的异常里面,rollback,如果没有异常或者异常不在配置异常里面,commit

事务注解什么情况下会失效

上面说了事务注解的简要原理。那么,首先能想到的失效的方法是啥?spring的@Transactional注解是根据异常来处理回滚事务或者提交的,那么,我要是把异常捕获,不就让注解失效了吗,以前看有人在service里面加上@Transactional,然后方法里调用操作数据库方法加上try catch,那样是没有用的,除非你捕获后再抛出去,那样就没啥意义了。

然后呢,还有其他事务失效的情况没?这里我说说我写这篇文章的动机,就是在看一些aop类的注解时,想到注解是隐藏很多逻辑,实际上处理注解编写代码很麻烦,那么,@Transactional的逻辑写在哪里呢?我能不能绕过这些逻辑让他失效呢?

绕过@Transactional注解处理逻辑让事务失效

编写简单的代码,详情见注释

 1 import org.springframework.beans.factory.annotation.Autowired;
 2 import org.springframework.stereotype.Controller;
 3 import org.springframework.web.bind.annotation.RequestMapping;
 4 import org.springframework.web.bind.annotation.ResponseBody;
 5 
 6 import com.example.demo.dao.StudentDao;
 7 import com.example.demo.pojo.Student;
 8 import com.example.demo.service.StudentService;
 9 import com.example.demo.service.impl.StudentServiceImpl;
10 
11 @Controller
12 @ResponseBody
13 public class StudentController {
14     
15     private StudentService studentService;
16     
17     // 注入service
18 //    @Autowired
19 //    public StudentController(StudentService studentService) {
20 //        this.studentService = studentService;
21 //    }
22     
23     // 手动创建service
24     @Autowired
25     public StudentController(StudentDao dao) {
26         this.studentService = new StudentServiceImpl(dao);
27     }
28 
29     @RequestMapping("get")
30     public Student find(Integer id) {
31         return studentService.findStudentByStudentId(id);
32     }
33     
34     @RequestMapping("insert")
35     public Student insertStudent(Student student) {
36         System.out.println(studentService.getClass());
37         
38         boolean res = studentService.insertStudentByData(student);
39         System.out.println(res + "------------------------------");
40         return student;
41     }
42     
43 }
StudentController.java
 1 import org.springframework.beans.factory.annotation.Autowired;
 2 import org.springframework.stereotype.Service;
 3 import org.springframework.transaction.annotation.Transactional;
 4 
 5 import com.example.demo.dao.StudentDao;
 6 import com.example.demo.pojo.Student;
 7 import com.example.demo.service.StudentService;
 8 
 9 @Service
10 public class StudentServiceImpl implements StudentService {
11 
12     private StudentDao studentMapper;
13     
14     @Autowired
15     public StudentServiceImpl(StudentDao studentMapper) {
16         this.studentMapper = studentMapper;
17     }
18     
19     @Override
20     public Student findStudentByStudentId(Integer id) {
21         return studentMapper.getStudentById(id);
22     }
23 
24     @Override
25     @Transactional
26     public boolean insertStudentByData(Student student) {
27         int insertStudent = studentMapper.insertStudent(student);
28         
29         // 手动制造异常,当年龄为0时报错 by zero
30         if (null != student && null != student.getAge()) {
31             System.out.println("====================" + (3 / student.getAge()));
32         }
33         if (insertStudent > 0) {
34             return true;
35         }
36         return false;
37     }
38 
39 }
StudentServiceImpl.java

首先我想到的是,注解生效,那是什么时候生效,如果我直接调用service,那么他begin()commit()逻辑写在哪里,不可能一个请求都用事务包裹起来,事务一般时细节到一个方法的,可是我service调用前后他没办法给我加上逻辑啊。会不会?会不会?会不会我的service根本就不是我的service呢?只是看起来是我的service,看一下数据库的数据

没有id为7这个student

curl调用

curl "localhost:8080/insert?id=7&name=byZero&age=0"

结果如下,报错了,并且加了注解,没有捕获异常,为啥事务没有生效

这里,其实我做了一点晓得改变,注入的service修改为手动new出的service,不使用spring管理的servi

// 手动创建service
@Autowired
public StudentController(StudentDao dao) {
	this.studentService = new StudentServiceImpl(dao);
}

看看我接下来的测试,为啥会失效相信看了下面的图就会明白了

手动创建service

上面是手动创建service

自动注入service

上面是注入service

不同方式出来的类不一样,注入的service是代理的service,在执行方法时会根据事务注解在前后加上begin rollback等逻辑,方便用户不用把begin这些编码进业务里面,当你要回滚时,直接抛出一个异常就可以了,方便编码。

总结

在现在很多框架减轻我们编码压力时,需要时常想一想他们大概时怎样实现的,如果是我我会怎么做,框架里的哪些设计模式以及编码习惯等我是否可以学。java开源轮子多,最好的学习资料都在哪些开源框架里面,不需要太深,但是要有基本的了解,没有黑魔法,都是一行行代码写出来的。

编码不断,学习不止,努力!!!

 

posted @ 2020-06-12 15:53  金陵小栗旬  阅读(419)  评论(0编辑  收藏  举报