分别测试四种情况:
- 同一个service中,A方法调用B方法,B方法抛异常
- 同一个service中,A方法调用B方法,A方法抛异常
- service1中A方法,调用service2中B方法,B方法抛异常
- service1中A方法,调用service2中B方法,A方法抛异常
分别测试A、B方法加上@Transactional注解
Controller层:
@RestController
@RequestMapping(value = "/transaction")
public class TransactionController {
@Autowired
TransactionService transactionService;
@GetMapping(value="/test")
public String test() {
return transactionService.test();
}
}
Dao层:
@Mapper
public interface TransactionDao {
@Insert(value = "insert into test(name) values ('小明')")
public void insertUser();
@Insert(value = "insert into test(name) values ('小李')")
public void insertAnotherUser();
}
Service1:
@Service
public class TransactionService {
@Autowired
TransactionDao transactionDao;
@Autowired
TransactionServiceB transactionServiceB;
public String test() {
transactionDao.insertUser();
return "success";
}
public void testException() {
transactionDao.insertAnotherUser();
throw new RuntimeException();
}
}
Service2:
@Service
public class TransactionServiceB {
public void testException() {
transactionDao.insertAnotherUser();
throw new RuntimeException();
}
}
测试地址:http://localhost:8080/transaction/test
一、同一个service中,A方法调用B方法,B方法抛异常
1.1 方法A加@Transactional注解,方法B不加@Transactional注解
Service:
@Transactional
public String test() {
transactionDao.insertUser();
testException();
return "success";
}
public void testException() {
transactionDao.insertAnotherUser();
throw new RuntimeException();
}
结果:
数据库:
事务成功回滚
1.2 方法A加@Transactional注解,方法B加@Transactional注解
Service:
@Transactional
public String test() {
transactionDao.insertUser();
testException();
return "success";
}
@Transactional
public void testException() {
transactionDao.insertAnotherUser();
throw new RuntimeException();
}
结果:
数据库:
事务成功回滚
1.3 方法A不加@Transactional注解,方法B加@Transactional注解
Service:
public String test() {
transactionDao.insertUser();
testException();
return "success";
}
@Transactional
public void testException() {
transactionDao.insertAnotherUser();
throw new RuntimeException();
}
结果:
数据库:
事务没有回滚,B方法上的@Transactional注解失效。由于中间重置了一下自增的序列,因此id是从3开始,下面可以看到正常的主键的id的跳跃
二、 同一个service中,A方法调用B方法,A方法抛异常
2.1 方法A加@Transactional注解,方法B不加@Transactional注解
Service:
@Transactional
public String test() {
transactionDao.insertUser();
testException();
throw new RuntimeException();
}
public void testException() {
transactionDao.insertAnotherUser();
}
结果:
数据库:
事务成功回滚
2.2 方法A加@Transactional注解,方法B加@Transactional注解
Serice:
@Transactional
public String test() {
transactionDao.insertUser();
testException();
throw new RuntimeException();
}
@Transactional
public void testException() {
transactionDao.insertAnotherUser();
}
结果:
数据库:
事务成功回滚
2.3 方法A不加@Transactional注解,方法B加@Transactional注解
Service:
public String test() {
transactionDao.insertUser();
testException();
throw new RuntimeException();
}
@Transactional
public void testException() {
transactionDao.insertAnotherUser();
}
结果:
数据库:
事务没有回滚,B方法上的@Transactional注解失效,同时自增也间隔了4,说明之前的4次插入数据的回滚,会影响自增的递增
三、 service1中A方法,调用service2中B方法,B方法抛异常
3.1 方法A加@Transactional注解,方法B不加@Transactional注解
Service1:
@Transactional
public String test() {
transactionDao.insertUser();
transactionServiceB.testException();
return "success";
}
Service2:
public void testException() {
transactionDao.insertAnotherUser();
throw new RuntimeException();
}
结果:
数据库:
事务回滚成功
3.2 方法A加@Transactional注解,方法B加@Transactional注解
Service1:
@Transactional
public String test() {
transactionDao.insertUser();
transactionServiceB.testException();
return "success";
}
Service2:
@Transactional
public void testException() {
transactionDao.insertAnotherUser();
throw new RuntimeException();
}
结果:
数据库:
事务回滚成功
3.3 方法A不加@Transactional注解,方法B加@Transactional注解
Service1:
public String test() {
transactionDao.insertUser();
transactionServiceB.testException();
return "success";
}
Service2:
@Transactional
public void testException() {
transactionDao.insertAnotherUser();
throw new RuntimeException();
}
结果:
数据库:
Service1中没有事务,也没有回滚,Service2中的事务成功回滚
四、 service1中A方法,调用service2中B方法,A方法抛异常
4.1 方法A加@Transactional注解,方法B不加@Transactional注解
Service1:
@Transactional
public String test() {
transactionDao.insertUser();
transactionServiceB.testException();
throw new RuntimeException();
}
Service2:
public void testException() {
transactionDao.insertAnotherUser();
}
结果:
数据库:
事务回滚成功
4.2 方法A加@Transactional注解,方法B加@Transactional注解
Service1:
@Transactional
public String test() {
transactionDao.insertUser();
transactionServiceB.testException();
throw new RuntimeException();
}
Service2:
@Transactional
public void testException() {
transactionDao.insertAnotherUser();
}
结果:
数据库:
事务回滚成功,同时可以说明,默认的传播行为REQUIRED,一会可以测试一下其他的传播机制
4.3 方法A不加@Transactional注解,方法B加@Transactional注解
Service1:
public String test() {
transactionDao.insertUser();
transactionServiceB.testException();
throw new RuntimeException();
}
Service2:
@Transactional
public void testException() {
transactionDao.insertAnotherUser();
}
结果:
数据库:
Service1中没有事务,也没有进行回滚
五、测试其他传播机制
5.1 采用4.2 方法A加@Transactional注解,方法B加@Transactional注解
进行测试,A方法抛出异常,修改方法B的传播机制
Service1:
@Transactional
public String test() {
transactionDao.insertUser();
transactionServiceB.testException();
throw new RuntimeException();
}
Service2:
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void testException() {
transactionDao.insertAnotherUser();
}
结果:
数据库:
可以看到,Service1中的事务成功回滚,Service2中由于新开启了一个事务,所以Service1中的回滚,并不会影响Service2中的数据的提交
5.2 采用3.2 方法A加@Transactional注解,方法B加@Transactional注解
进行测试,B方法抛出异常,修改方法B的传播机制
Service1:
@Transactional
public String test() {
transactionDao.insertUser();
transactionServiceB.testException();
return "success";
}
Service2:
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void testException() {
transactionDao.insertAnotherUser();
throw new RuntimeException();
}
结果:
数据库:
Service1和Sevice2中的事务都成功回滚
再进行一个尝试,A方法中捕获一下异常
Service1:
@Transactional
public String test() {
transactionDao.insertUser();
try {
transactionServiceB.testException();
} catch (Exception e) {
return e.getMessage();
}
return "success";
}
Service2:
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void testException() {
transactionDao.insertAnotherUser();
throw new RuntimeException();
}
结果:
返回空
数据库:
Service1中捕获了异常,因此Service1中的事务可以正常提交,Service2中由于出现异常,事务进行了回滚
六、 总结
同一个类中,A方法调用B方法,此时B方法的@Transactional注解是无效的,因为没有经过Spring的代理,直接对方法进行了调用,所以注解没有办法被AOP扫描到,就不能实现事务的功能。
不同类中,A方法调用B方法,此时B方法的@Transactional注解是生效的,因为经过了Spring的代理。此时A方法或者B方法中出现异常,是否回滚需要看事务的传播行为,以及异常是否进行了捕获。