此参数描述如下:
该属性用于设置当前事务是否为只读事务,设置为true表示只读,false则表示可读写,默认值为false。例如:@Transactional(readOnly=true)
按照这个说法我们来验证一下

可见该情况下查询没有问题。我们再看一下在里面写更新语句。

所以很明显可以看出,标注了该属性,你就只能执行查询类语句。
此参数描述如下:
该属性用于设置需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,则进行事务回滚。例如:@Transactional(rollbackFor={RuntimeException.class, Exception.class})
可以了解到,这个参数就是用来控制 @Transactional 注解能回滚的异常类型的,下面举个例子,我会新建一个异常,指定抛出该异常就会回滚。
先看数据库表中数据:

我新建的异常类:

看写法

执行结果:

可见其更新成功了,并且抛出了坤坤异常,且数据库中数据并没有被改掉,所以我们配置的指定坤坤异常回滚生效了。
但是其中还是有点要注意的地方,就是你如果使用try catch 捕获了异常,没有让它抛出来,那么是不会回滚的,数据库的值仍然会被改掉 !!!!!!
如下图:

你自己将异常捕获,那么便会无法回滚。
最后补充一点:

它默认在RuntimeException和Error上回滚,所以要特别注意你代码中会产生的异常是不是在这两类中。例如下图,如果你抛出了IOException,且没有指定回滚类型,那么事务便会失效。

所以一般情况下,我会这么写:@Transactional(rollbackFor = Exception.class)
rollbackForClassName,noRollbackFor,noRollbackForClassName与此类似,在此不做赘述。
该属性用于设置事务的超时秒数,默认值为-1表示永不超时。
这个没啥好说的,自己试一下即可。
该属性用于设置事务的传播行为,较为常用的便是这个了。
Propagation.REQUIRED
Propagation.SUPPORTS
Propagation.MANDATORY
Propagation.REQUIRES_NEW
Propagation.NOT_SUPPORTED
Propagation.NEVER
Propagation.NESTED
调用test3()进行调试
调试代码:
@Service
public class ITestServiceImpl implements ITestService {
private static final String QUERY_ALL = "select * from zzp_test_tx";
private static final String ZHANG_SAN = "update zzp_test_tx set ACCOUNT = 90 where NAME='zhangsan'";
private static final String LI_SI = "update zzp_test_tx set ACCOUNT = 90 where NAME='lisi'";
private static final String WANG_WU = "update zzp_test_tx set ACCOUNT = 90 where NAME='wangwu'";
private static final String ZHAO_SI = "update zzp_test_tx set ACCOUNT = 90 where NAME='zhaosi'";
@Autowired
private JdbcTemplate jdbcTemplate;
@Autowired
@Lazy
private ITestService iTestService;
@Transactional(propagation = Propagation.REQUIRED)
@Override
public Object test1() {
//更新王五数据
jdbcTemplate.update(WANG_WU);
return 1;
}
@Override
@Transactional(propagation = Propagation.REQUIRED)
public Object test2() {
//更新赵四数据
jdbcTemplate.update(ZHAO_SI);
return 1;
}
@Transactional
@Override
public Object test3() throws Exception {
/*
按照 REQUIRED 的意义分析:
如果外层 test3() 开启了事务,
则 test2() 和 test1() 都加入 test3() 的事务中,属于同一个事务。
否则,test2() 和 test2() 则都会创建一个自己的事务,属于分开的两个事务。
*/
//注意这里的调用方式,我是自己注入自己调用的,也可以通过spring获取该Bean的实例调用,
//直接this调用,不管怎么配置的事务,事务都没有用。
iTestService.test1();
iTestService.test2();
//按照分析,调用方法 test3 , 这里抛出异常,那更新的两条数据都会回滚。
if (1 == 1) {
throw new RuntimeException();
}
return 1;
}
}
按上图说明所示,test3()加上@Transactional,那么这三个方法在同一个事务里面,出错一起回滚。
然后将test3()上的@Transactional去掉,也就是test3()不加事务,此时 test2() 和 test1()是独立的一个事务。
也就是,按照上述代码,test3()抛出异常,不会使test()2和test1()回滚,但是你在test1()里面抛出异常,test1()的回滚会导致test2()回滚,因为test1() 和 test2()是同一个事务 。
有人会问,为什么test3()没加事务,test1()和test2()还是一个事务呢?
很好理解,Propagation.REQUIRED 的定义就是支持当前事务,执行时发现test1()有事务,test2()自动就加入了test1()的事务。
有了上面的分析,我们在来看 Propagation.SUPPORTS 我们再再次回顾一下它的含义:支持当前事务,如果当前没有事务,就以非事务方式执行。
所以还是上面的代码,我们把 test1() 和 test2() 上的注解换成 @Transactional(propagation = Propagation.SUPPORTS),
同时我们:
test3() 加上@Transactionaltest1()和test2()和test3()仍然是在一个事务里面,不论谁出错都会一起回滚。test3()加@Transactional同样的,我们再再次回顾一下Propagation.MANDATORY的含义:支持当前事务,如果当前没有事务,就抛出异常。
这个验证起来就简单了,我们把 test1() 和 test2() 上的注解换成 @Transactional(propagation = Propagation.MANDATORY)
对于 test3() :
不加@Transactional
方法test3()调用即报错。

加@Transactional
同样的三个方法在一个事务中,不论谁抛异常,一起回滚。
同样的,我们再再次回顾一下Propagation.REQUIRES_NEW的含义:新建事务,如果当前存在事务,把当前事务挂起。
用以上的三个方法举例,我们还是先将 test1() 和 test2() 上的注解换成 @Transactional(propagation = Propagation.REQUIRES_NEW),那么按照含义来说,不论我的 test3()使用或者不使用@Transactional,test1()和test2()都是各自单独的事务。
也就是说:
test3()使用了事务,test3()抛异常,不会导致 test1() 和 test2()的回滚test1()和test2()是各自的事务,他俩之间也不会相互影响,谁抛异常谁自己回滚,也即是test1()抛异常回滚,test2()还是正常执行。)。同样的,我们再再次回顾一下Propagation.NOT_SUPPORTED的含义:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
这个很好理解,还是用上面三个方法举例子,首先我们还是先将 test1() 和 test2() 上的注解换成 @Transactional(propagation = Propagation.NOT_SUPPORTED)
此时test3()加不加@Transactional ,test1()和test2()都不会有任何事务。
此时可能有人会问,我的 test3() 加了@Transactional 那么 test3() 里面的更新操作会不会回滚呢。咱们试试看:
@Service
public class ITestServiceImpl implements ITestService {
private static final String QUERY_ALL = "select * from zzp_test_tx";
private static final String ZHANG_SAN = "update zzp_test_tx set ACCOUNT = 90 where NAME='zhangsan'";
private static final String LI_SI = "update zzp_test_tx set ACCOUNT = 90 where NAME='lisi'";
private static final String WANG_WU = "update zzp_test_tx set ACCOUNT = 90 where NAME='wangwu'";
private static final String ZHAO_SI = "update zzp_test_tx set ACCOUNT = 90 where NAME='zhaosi'";
@Autowired
private JdbcTemplate jdbcTemplate;
@Autowired
@Lazy
private ITestService iTestService;
@Transactional(propagation = Propagation.NOT_SUPPORTED)
@Override
public Object test1() {
//更新王五数据
jdbcTemplate.update(WANG_WU);
return 1;
}
@Override
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public Object test2() {
//更新赵四数据
jdbcTemplate.update(ZHAO_SI);
return 1;
}
@Transactional
@Override
public Object test3() throws Exception {
/*
按照 NOT_SUPPORTED 的定义,下面这种情况,会回滚的就只有 zhangsan 的更新操作
*/
iTestService.test1();
iTestService.test2();
//更新张三
jdbcTemplate.update(ZHANG_SAN);
if (1==1){
throw new RuntimeException();
}
return 1;
}
}
结果是:test3()抛异常,test1()和test2()的数据没回滚,test3()更新zhangsan的操作还是正常回滚了。
同样的,我们再再次回顾一下Propagation.NEVER的含义:以非事务方式执行,如果当前存在事务,则抛出异常。
这个验证起来就简单了,
我们把 test1() 和 test2() 上的注解换成 @Transactional(propagation = Propagation.NEVER)
对于 test3() :
@Transactionaltest3()调用即报错。
同样的,我们再再次回顾一下Propagation.NEVER的含义:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。
这个可能比较奇怪,还是先描述一下:
我们先把 test1() 和 test2() 上的注解换成 @Transactional(propagation = Propagation.NESTED)
对于 test3() :
@Transactional,那么test1() 和test2()便会创建嵌套事务,什么是嵌套事务呢?也就是 内部异常不会导致外部回滚,外部回滚会导致内部回滚。举个例子来说,就是这种情况下,test3() 里面抛异常,test1() 和 test2()会一起回滚,但是 test1() 或者 test2()抛出异常,并不会导致test3()中的更新操作回滚。(注意内部异常情况,你的test1()或test2()异常不可向上抛出,否则会导致test3()感知到异常,导致外部也跟着一起回滚了。或者说,你的test1()或test2()异常向上抛出,在test3()里面必须手动trycatch抛异常的方法。)。看示例代码吧:

说实话,这其实就是在test3中,强行吃掉了test2()的异常,也就是让test3()检测不到任何异常,此外,由于test2有嵌套的事务test2()它自己会回滚,从而不影响其他更新方法。
说实话,我属实也是没想到 Propagation.NESTED 的应用场景。。
@Transactional,那么test1()和test2()会各自新建自己事务执行。此时test3()没有任何事务,它自己的更新操作即使出错了也不会回滚。该属性用于设置底层数据库的事务隔离级别,事务隔离级别用于处理多事务并发的情况,通常使用数据库的默认隔离级别即可,基本不需要进行设置
首先了解一下数据库中可能会发生的脏读等问题:
可见这篇文章 这里不做过多演示!
Spring的隔离级别:
Isolation.DEFAULT:
Isolation.READ_UNCOMMITTED:
Isolation.READ_COMMITTED:
Isolation.REPEATABLE_READ:
Isolation.SERIALIZABLE :
答: 既然是封装,那么Spring项目应该就是以Spring事务为准的,除非使用 @Transactional(isolation = Isolation.DEFAULT)时,才会使用数据库设置的隔离级别。
下面开始一个简单的验证:
先看一下MYSQL8数据库的隔离级别:

可见是可重复读。 接下来在spring事务指定为读未提交,看看究竟使用的哪个。

由图中可见很明显不是可重复读的隔离级别。
为了防止误判,我们把它改成@Transactional(isolation = Isolation.DEFAULT)再试一次。(重试之间记得把数据改回原来的样子)

可见,就是可重复读。所以就是 当spring隔离级别和数据库隔离级别不一样时,spring的优先