测试环境:
数据库
StudnetMapper接口,TeacherMapper同理。
package com.cherish.transaction.mapper;
import com.cherish.transaction.entity.Student;
import org.apache.ibatis.annotations.Insert;
public interface StudentMapper {
@Insert("insert into student(name,age) values(#{name},#{age})")
int addStudent(Student student);
}
StudnetService接口,TeacherService同理。
package com.cherish.transaction.service;
import com.cherish.transaction.entity.Student;
public interface StudentService {
int insertStudent(Student student);
}
StudentService 实现类,TeacherService同理
package com.cherish.transaction.service.impl;
import com.cherish.transaction.entity.Student;
import com.cherish.transaction.entity.Teacher;
import com.cherish.transaction.mapper.StudentMapper;
import com.cherish.transaction.mapper.TeacherMapper;
import com.cherish.transaction.service.StudentService;
import com.cherish.transaction.service.TeacherService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class StudentServiceImpl implements StudentService {
@Autowired
private StudentMapper studentMapper;
@Override
public int insertStudent(Student student) {
System.out.println("TeacherServiceImpl insertTeacher 开始执行"+student);;
int i = studentMapper.addStudent(student);
System.out.println("TeacherServiceImpl insertTeacher 执行结束");
return i;
}
}
设计DoAllService调用者两个service
package com.cherish.transaction.service;
import com.cherish.transaction.entity.Student;
import com.cherish.transaction.entity.Teacher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class DoAllService {
@Autowired
private TeacherService teacherService;
@Autowired
private StudentService studentService;
public void doAll(){
Teacher teacher = new Teacher("王老师");
int i = teacherService.insertTeacher(teacher);
System.out.println("中间");
Student student = new Student("小王");
int i1 = studentService.insertStudent(student);
}
}
1、在三个service上的三个方法打上如下注解
@Transactional(propagation= Propagation.REQUIRED)、
1、修改
启动了老师服务的事务,抛出异常后,回滚了老师插入的操作,但是学生service的插入学生操作未执行(因为中间未打印),抛出异常后,没有执行下去
反着修改,在学生中抛出异常,老师中正常插入操作(规避抛出异常不执行,因为先执行了正常的老师操作操作)
结果:
结果:学生和老师都没插入
所以:在平时使用中,某个controller中需要使用多个service做DML操作,可以实现一个中间service(例子中的doAll),在中间service中去注入A、B等等业务service(各个业务service也是存在自己的事物的),在这种场景下,就能将两个业务service控制在同一个service中
@Transactional(propagation= Propagation.REQUIRED)
public void doAll(){
Teacher teacher = new Teacher("王老师");
int i = teacherService.insertTeacher(teacher);
System.out.println("中间");
Student student = new Student("小王");
int i1 = studentService.insertStudent(student);
}
2.修改studentService方法上的注解为
@Transactional(propagation= Propagation.REQUIRES_NEW)
再次测试的结果是
两个操作都未成功
原因:学生服务插入操作由于是
@Transactional(propagation= Propagation.REQUIRES_NEW)
Propagation.REQUIRES_NEW挂起原有事物,新建一个事务,这两个数据库连接也是不同的,此时学生服务操作失败是必然的,而老师服务也被回滚是因为,老师服务和doAll服务在同一个事务内,学生抛出的运行时异常在doAll中被发现,所以出发了该事务的回滚,所有老师操作被回滚,也失效。
3、在doAll中捕获学生服务的异常
结果老师插入成功了,学生操作还是失效
原因:在前面的例子中,只是将学生抛出的异常给捕获处理了,在doAll和老师服务这个事务中,并未发现异常,所有操作都是正常的,这样就不会出现回滚,而学生服务依然是在自己新建的事务中回滚。
4、将学生服务修改为
@Transactional(propagation= Propagation.NESTED)
因为学生服务是作为doAll服务的子事务(嵌套事务),在学生服务中回滚是回滚到还原点处,也就是插入老师后,而插入老师没有抛出异常,所以是正常执行,插入成功。而插入学生依然是失败,其中这里在doAll方法中依然是需要try catch学生服务的。
对比
@Transactional(propagation= Propagation.REQUIRED_NEW)和@Transactional(propagation= Propagation.NESTED)
一个是新的独立事务,一个是嵌套的子事务,功能类似
下面为网络上复制的一段,仅供参考
在 SPRING 中一共定义了七种事务传播属性
-
PROPAGATION_REQUIRED -- 支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
-
PROPAGATION_SUPPORTS -- 支持当前事务,如果当前没有事务,就以非事务方式执行。
-
PROPAGATION_MANDATORY -- 支持当前事务,如果当前没有事务,就抛出异常。
-
PROPAGATION_REQUIRES_NEW -- 新建事务,如果当前存在事务,把当前事务挂起。
-
PROPAGATION_NOT_SUPPORTED -- 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
-
PROPAGATION_NEVER -- 以非事务方式执行,如果当前存在事务,则抛出异常。
-
PROPAGATION_NESTED -- 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。
下面为,一些源码上的debug
进入doLogin方法,实现类DataSourceTransactionManager中的doLogin方法如下
以上的截图没有其他目的,就是给自己以后复习提个醒,这些类、方法、位置需要留意。同时发现打上事务注解后,代理对象带上了CGLIB的标识,应该就是通过CGLIB方式生成代理对象,对事务切面处理。
下面是一个特殊例子:
package com.cherish.transaction.service.impl;
import com.cherish.transaction.entity.Student;
import com.cherish.transaction.entity.Teacher;
import com.cherish.transaction.mapper.StudentMapper;
import com.cherish.transaction.mapper.TeacherMapper;
import com.cherish.transaction.service.StudentService;
import com.cherish.transaction.service.TeacherService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Service
public class StudentServiceImpl implements StudentService {
@Autowired
private StudentMapper studentMapper;
@Autowired
@Transactional(propagation= Propagation.REQUIRED)
@Override
public int insertStudent(Student student) {
System.out.println(this);
int i1 = this.addStudent(student);
System.out.println("TeacherServiceImpl insertTeacher 开始执行"+student);;
int i = studentMapper.addStudent(student);
System.out.println("TeacherServiceImpl insertTeacher 执行结束");
int exception = 1/0;
return i;
}
@Transactional(propagation= Propagation.REQUIRES_NEW)
public int addStudent(Student student){
System.out.println("TeacherServiceImpl addStudent 开始执行"+student);;
int i = studentMapper.addStudent(student);
System.out.println("TeacherServiceImpl addStudent 执行结束");
return i;
}
}
package com.cherish.transaction;
import com.cherish.transaction.entity.Student;
import com.cherish.transaction.entity.Teacher;
import com.cherish.transaction.service.DoAllService;
import com.cherish.transaction.service.StudentService;
import com.cherish.transaction.service.TeacherService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class TestTransaction02 {
@Autowired
private StudentService studentService;
@Test
public void test01(){
Student student = new Student("lys");
studentService.insertStudent(student);
}
}
结果两个数据插入没成功
this对象输出为
原因:
@Transactional(propagation= Propagation.REQUIRES_NEW) public int addStudent(Student student){
未生效,根据前面分析,如果上面Propagation.REQUIRES_NEW生效了,则是会出现,insert生效,add不生效,数据库插入一条lys的数据。
修改方法
@Override
@Transactional(propagation= Propagation.REQUIRED)
public int insertStudent(Student student) {
System.out.println("this "+this);
StudentService studentService = applicationContext.getBean(StudentService.class);
System.out.println("studentService "+studentService);
int i1 = studentService.addStudent(student);
System.out.println("TeacherServiceImpl insertTeacher 开始执行"+student);;
int i = studentMapper.addStudent(student);
System.out.println("TeacherServiceImpl insertTeacher 执行结束");
int exception = 1/0;
return i;
}
结果:插入一条lys数据成功
输出结果为
方法二:
获取当前代理对象
报错:
解决:
@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true)
运行结果:
添加
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
执行结果:
插入数据成功,因为
@Transactional(propagation= Propagation.REQUIRES_NEW) public int addStudent(Student student){
是自己的新事物,不会因为insertStudent方法中抛出异常给被动回滚掉
反证:注释该注解
// @Transactional(propagation= Propagation.REQUIRES_NEW) public int addStudent(Student student){
执行结果:
两条记录都为生效,
public int addStudent(Student student){方法调用和将方法内的代码写入addStudent方法中无异,所以必然是同一个事务内,这样在出现除0异常后,也就一起回滚了,最总两个操作都失效。
总结两种方法
@Override
@Transactional(propagation= Propagation.REQUIRED)
public int insertStudent(Student student) {
System.out.println("this "+this);
// 失效
// int i1 = this.addStudent(student);
// System.out.println("this "+this);
// 失效
// 解决方法一 注:applicationContext是注入的容器上下文对象
// StudentService studentService = applicationContext.getBean(StudentService.class);
// System.out.println("studentService "+studentService);
// 解决方法二
// 注: 1、需要在spirng boot启动类上需要配置上述的EnableAspectJAutoProxy 2、需要导入spring-boot-starter-aop 包
StudentService studentService = (StudentService)AopContext.currentProxy();
int i1 = studentService.addStudent(student);
System.out.println("中间");
System.out.println("TeacherServiceImpl insertTeacher 开始执行"+student);;
int i = studentMapper.addStudent(student);
System.out.println("TeacherServiceImpl insertTeacher 执行结束");
int exception = 1/0;
return i;
}
以上是自己对spring事务的进一步学习的记录,给自己做个笔记,同时些许能帮助部分同学理解Spring事务