Spring中,@Transactional 注解的失效测试以及传播行为测试

分别测试四种情况:

  1. 同一个service中,A方法调用B方法,B方法抛异常
  2. 同一个service中,A方法调用B方法,A方法抛异常
  3. service1中A方法,调用service2中B方法,B方法抛异常
  4. 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方法中出现异常,是否回滚需要看事务的传播行为,以及异常是否进行了捕获。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章