Spring编程常见错误–Spring补充篇-19 |Spring 事务常见错误(上)

十九、Spring 事务常见错误(上)

Spring 事务管理包含两种配置方式,第一种是使用 XML 进行模糊匹配,绑定事务管理;第二种是使用注解,这种方式可以对每个需要进行事务处理的方法进行单独配置,你只需要添加上 @Transactional,然后在注解内添加属性配置即可。

一.unchecked 异常与事务回滚(@Transactional默认只回滚RuntimeException 或者 Error,需要进行配置,才能触发Exception回滚)

Spring 处理事务的时候,如果没有在 @Transactional 中配置 rollback 属性,那么只有捕获到 RuntimeException 或者 Error 的时候才会触发回滚操作。而我们案例抛出的异常是 Exception,又没有指定与之匹配的回滚规则,所以我们不能触发回滚。

1、SQL表结构

1.学生表
CREATE TABLE `student` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `realname` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
2.学生选课表 student_course,记录学生表 student 和课程表 course 之间的多对多关联。
CREATE TABLE `student_course` (
  `student_id` int(11) NOT NULL,
  `course_id` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

2、Service

@Service
public class StudentService {
    @Autowired
    private StudentMapper studentMapper;

    @Transactional
    public void saveStudent(String realname) throws Exception {
        Student student = new Student();
        student.setRealname(realname);
        studentMapper.saveStudent(student);
        if (student.getRealname().equals("小明")) {
            throw new Exception("该学生已存在");
        }
    }
}

3、TEST

public class AppConfig {
    public static void main(String[] args) throws Exception {
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        StudentService studentService = (StudentService) context.getBean("studentService");
        studentService.saveStudent("小明");
    }
}

虽然发生了异常,代码中断了,但是数据库还是添加了数据,这个不是我们想要的结果。

4、案例解析

pring 处理事务的时候,如果没有在 @Transactional 中配置 rollback 属性,那么只有捕获到RuntimeException 或者 Error 的时候才会触发回滚操作。而我们案例抛出的异常是 Exception,又没有指定与之匹配的回滚规则,所以我们不能触发回滚。

5、问题修正

指定rollback的Exception类型

  @Transactional(rollbackFor = Exception.class)
@Service
public class StudentService {
    @Autowired
    private StudentMapper studentMapper;
    
    @Transactional(rollbackFor = Exception.class)
    public void saveStudent(String realname) throws Exception {
        Student student = new Student();
        student.setRealname(realname);
        studentMapper.saveStudent(student);
        if (student.getRealname().equals("小明")) {
            throw new RuntimeException("该用户已存在");
        }
    }

二.试图给 private 方法添加事务(只有当注解为事务的方法被声明为 public 的时候,才会被 Spring 处理)

1、Service


@Service
public class StudentService {
    @Autowired
    private StudentMapper studentMapper;

    @Autowired
    private StudentService studentService;

    public void saveStudent(String realname) throws Exception {
        Student student = new Student();
        student.setRealname(realname);
        studentService.doSaveStudent(student);
    }

    @Transactional
    private void doSaveStudent(Student student) throws Exception {
        studentMapper.saveStudent(student);
        if (student.getRealname().equals("小明")) {
            throw new RuntimeException("该用户已存在");
        }
    }
}

异常正常抛出,事务却没有回滚。

2、案例解析

源码比较复杂,暂时记住结果就好。
事务生效的条件有以下几个

1.:allowPublicMethodsOnly()

allowPublicMethodsOnly 返回了 AnnotationTransactionAttributeSource 的 publicMethodsOnly 属性。


protected boolean allowPublicMethodsOnly() {
   return this.publicMethodsOnly;
}

而这个 publicMethodsOnly 属性是通过 AnnotationTransactionAttributeSource 的构造方法初始化的,默认为 true。

2.Modifier.isPublic()

这个方法根据传入的 method.getModifiers() 获取方法的修饰符。该修饰符是 java.lang.reflect.Modifier 的静态属性,对应的几类修饰符分别是:PUBLIC: 1,PRIVATE: 2,PROTECTED: 4。这里面做了一个位运算,只有当传入的方法修饰符是 public 类型的时候,才返回 true。

只有当注解为事务的方法被声明为 public 的时候,才会被 Spring 处理。

4、问题修正

将方法指定为public


@Service
public class StudentService {
    @Autowired
    private StudentMapper studentMapper;

    @Autowired
    private StudentService studentService;

    public void saveStudent(String realname) throws Exception {
        Student student = new Student();
        student.setRealname(realname);
        studentService.doSaveStudent(student);
    }

    @Transactional
    public void doSaveStudent(Student student) throws Exception {
        studentMapper.saveStudent(student);
        if (student.getRealname().equals("小明")) {
            throw new RuntimeException("该学生已存在");
        }
    }
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值