1.开启事物
通过注解开启
@EnableTransactionManagement
提示: Spring Boot 以Spring 5.0为基础的版本无需注解自动开启事物
2.测试事物
首先查询数据库中的表
默认表中有一条数据;
2.1默认不用事物
首先是不开启,创建两个数据代码如下:
public void test() {
SystemMenu m1=new SystemMenu();
m1.setName("test2");
m1.setTerminal("00");
menuExtMapper.insertSelective(m1);
SystemMenu m2=new SystemMenu();
m2.setId(1);//重复ID 数据库会报错
m2.setName("test3");
m2.setTerminal("00");
}
执行上面的代码:
通过控制台我们可以看到已经输出了 主键冲突的异常,我们再看看数据库:
通过数据库查询可以看到,test2数据已经存入数据库,test3肯定就是失败了,同时证明了默认没开启事物,遇到错误并不会回滚。
2.2通过注解@Transactional开启事物
首先我们将数据库还原,删除掉test2数据,如下:
程序代码如下:
@Transactional
public void test() {
SystemMenu m1=new SystemMenu();
m1.setName("test2");
m1.setTerminal("00");
menuExtMapper.insertSelective(m1);
SystemMenu m2=new SystemMenu();
m2.setId(1);//重复ID 数据库会报错
m2.setName("test3");
m2.setTerminal("00");
menuExtMapper.insertSelective(m2);
}
执行上方代码:
从控制台看,同样输出了主键冲突的错误,我们再看看数据库中的数据:
从数据可以看到,并没有数据存入。这里也就证明了事物开启成功,遇到错误就会同一回滚保证了数据的一致性。
提示:默认情况下,@Transactional注解只对RuntimeException及其子类异常有效。
证明上方的观点,下面以代码示例:
@Transactional
public void test() throws Exception {
SystemMenu m1=new SystemMenu();
m1.setName("test2");
m1.setTerminal("00");
menuExtMapper.insertSelective(m1);
if (1==1){
throw new Exception("EEEEEE");
}
SystemMenu m2=new SystemMenu();
m2.setId(1);//重复ID 数据库会报错
m2.setName("test3");
m2.setTerminal("00");
menuExtMapper.insertSelective(m2);
}
上方代码,在处理数据中间主动抛出了Exception异常,执行代码观察控制台以及数据库:
通过上方我们可以看到,抛出Exception的情况下事物并没有生效,test2已经存入数据库中。
提示:
可以通过注解@Transactional中的rollbackFor 来指定事物遇到那些异常会启用,同样也可以通过noRollbackFor指定那些异常不会滚
例如下方的配置事物遇到所有的Exception都会回滚
@Transactional(rollbackFor = {Exception.class})
2.3 启用事物的方法内部调用其他方法出现异常会回滚吗
首先我们蒋数据库恢复如下:
编写测试代码如下:
@Transactional(rollbackFor = {Exception.class})
public void test() throws Exception {
SystemMenu m1=new SystemMenu();
m1.setName("test2");
m1.setTerminal("00");
menuExtMapper.insertSelective(m1);
oo();
}
public void oo(){
SystemMenu m2=new SystemMenu();
m2.setId(1);
m2.setName("test2");
m2.setTerminal("00");
menuExtMapper.insertSelective(m2);
}
执行代码,观察控制台和数据库:
通过上方实验,我们可以看到事物生效了。所以内部调用其他方法出现异常同样会触发事物
提示:
调用的方法需没有标注注解@Transactional,如果标注注解会有几种情况,后方说明。
2.4 启用事物的方法内部调用其他启用事物的方法
编写测试代码1:
@Transactional(rollbackFor = {Exception.class})
public void test() throws Exception {
oo();
SystemMenu m1=new SystemMenu();
m1.setId(1);
m1.setName("test2");
m1.setTerminal("00");
menuExtMapper.insertSelective(m1);
}
@Transactional
public void oo(){
SystemMenu m2=new SystemMenu();
m2.setName("test3");
m2.setTerminal("00");
menuExtMapper.insertSelective(m2);
}
执行测试代码并观察控制台和数据库:
通过上方实验,我们可以知道默认情况下,@Transactional注解的方法调用@Transactional注解的方法也是可以事物统一回滚的。
注意:
- 调用的方法上指定的Exception范围必须包含被调用方抛出异常的范围,否则可能出现不一致
- 上方还涉及到一个知识点,事物的传播级别,默认情况下事物的传播级别为"REQUIRED",即:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。
所以上方两个方法的事物会被整合成一个事物处理。@Transactional注解也可以通过propagation 来指定传播级别,如下:
@Transactional(propagation = Propagation.REQUIRED)
事物传播的几个级别分别为:
- REQUIRED(@Transactional注解默认值): 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。
- SUPPORTS:前方法不需要事务上下文,但是如果存在当前事务的话,那么这个方法会在这个事务中运行。
- MANDATORY:该方法必须在事务中运行,如果当前事务不存在,则会抛出一个异常。不会主动开启一个事务。
- REQUIRES_NEW:前方法必须运行在它自己的事务中。一个新的事务将被启动,如果存在当前事务,在该方法执行期间,当前事务会被挂起(如果一个事务已经存在,则先将这个存在的事务挂起)。如果使用JTATransactionManager的话,则需要访问TransactionManager。
- NOT_SUPPORTED:该方法不应该运行在事务中,如果存在当前事务,在该方法运行期间,当前事务将被挂起。如果使用JTATransactionManager的话,则需要访问TransactionManager。
- NEVER:表示当前方法不应该运行在事务上下文中,如果当前正有一个事务在运行,则会抛出异常。
- NESTED:如果当前已经存在一个事务,那么该方法将会在嵌套事务中运行。嵌套的事务可以独立于当前事务进行单独地提交或回滚。如果当前事务不存在,那么其行为与REQUIRED一样。嵌套事务一个非常重要的概念就是内层事务依赖于外层事务。外层事务失败时,会回滚内层事务所做的动作。而内层事务操作失败并不会引起外层事务的回滚。
(未完待续)后续继续更新实验….
原创文章,作者:kepupublish,如若转载,请注明出处:https://blog.ytso.com/243756.html