zoukankan      html  css  js  c++  java
  • 记录一次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开源轮子多,最好的学习资料都在哪些开源框架里面,不需要太深,但是要有基本的了解,没有黑魔法,都是一行行代码写出来的。

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

  • 相关阅读:
    pytest
    pytest 跳过测试用例 skip
    pytest
    pytest -allure标记用例级别severity
    pytest-
    pytest-分布式执行(pytest-xdist)
    4、win10搭建easy-mock平台
    pandas日期缺失,空值填充方法
    Pandas拼接操作(concat,merge,join和append)的区别
    关于docker容器和镜像的区别
  • 原文地址:https://www.cnblogs.com/xiaolixun/p/13071816.html
Copyright © 2011-2022 走看看