一、事务相关概念
1、事务的特点
原子性:事务是一个原子操作,由一系列的动作完成,这一系列动作要么全部完成,要么全部都不完成。
一致性:一旦事务完成,事务建立的业务必须处于一致状态。
隔离性:针对很多事务处理相同的数据,事务之间必须隔离起来,不能够产生数据之间的相互影响。
持久性:一旦事务完成,那么事务建立的状态必须长久的保留下去,不能够相互影响。
2、基本事务属性定义
事务属性是什么?事务属性可以理解成事务的一些基本配置,表示了事务属性如何运用到方法上。
(1)事务的第一个行为是传播行为,当事务被另一个事务调用的时候,必须指定事务应该如何传播。例如方法可以在现有事务中运行,也可以开启一个新事务。Spring定义了七种传播行为,如下:
PROPAGATION_REQUIRED:表示当前方法必须在事务中运行,如果当前事务存在,就在当前事务中运行,如果不存在,则从新开启一个新事务。
PROPAGATION_SUPPORTS:表示当前方法不需要事务上下文,如果当前事务存在,则在当前事务中运行。
PROPAGATION_MANDATORY:表示当前方法必须在事务中运行,如果事务不存在,则会抛出异常。
PROPAGATION_REQUIRED_NEW:表示当前方法必须在自己的事务中运行。如果一个新的事务将被启动。如果存在当前事务,当前事务将被挂起。
PROPAGATION_NEVER:表示当前方法不应该运行在失误上下文中,如果存在当前事务,则会抛出异常。
PROPAGATION_NESTED:表示如果已经存在一个事物,那么该方法会嵌套在事务中运行。嵌套事务可以独立于当前事务单独地进行提交和回滚。
在此注意区分PROPAGATION_REQUIRED_NEW和PROPAGATION_NESTED,前者的两个事务,外层事务和内层事务是相互独立的两个事务,也就是内层事务的回滚不会影响外层事务,同时外层事务的回滚也不会影响内层事务。而对于后者,内层事务是依赖于外层事务的,也就是内层事务的回滚不会造成外层事务的回滚,而外层事务的回滚要造成内层事务的回滚。
(2)隔离级别
事务的第二个级别就是隔离级别,隔离级别定义了一个事务受其他并发事务的影响程度。
并发事务通常可能引起如下的问题:
脏读:脏读发生在一个事务读取了另一个事务改写了但是还没有提交的数据,那么这个时候如果另一个事务对数据进行了回滚,事务读取的数据就是无效的。
不可重复读:不可重复读是一个事务执行相同的查询两次以上,但是前后两次读取到的数据是不相同的,那是因为在两次读取数据的间歇中间,另一个事务对数据进行了修改。
幻读:幻读和不可重复读类似。发生在一个事务读取了几行数据,另一个事务又插入了几行数据,在随后的查询中将会发现数据发生了改变。
不可重复读和幻读的区别在于不可重复读重在修改,而欢度在于添加和删除。
具体的隔离级别如下:
ISOLATION_DEFAULT:使用数据库后端默认的隔离级别。
ISOLATION_READ_UNCOMMITED:最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、不可重复读和幻读。
ISOLATION_READ_COMMITED:允许读取并发事务提交的数据变更,可以防止脏读,但是不能够阻止不可重复读和幻读。
ISOLATION_REPEATABLE_READ:对同一字段多次读取的结果是一样的,除非事务本身修改了数据。可以防止脏读和不可重复读。
ISOLATION_SERIALIZABLE:最高的隔离级别,可以防止脏读、不可重复读和幻读。
(3)事务的只读属性
事务的第三个特性是它是否为只读事务。如果事务只对后端的数据库进行该操作,数据库可以利用事务的只读特性来进行一些特定的优化。通过将事务设置为只读,你就可以给数据库一个机会,让它应用它认为合适的优化措施。
(4)事务超时
为了使应用程序很好地执行,事务不能运行太长的时间。因为事务可能涉及对后端数据库的锁定,所以长时间的事务会不必要地占用数据资源。事务超
时是事务的一个定时器,在特定的时间内事务如果没有执行完毕,那么久自动回滚,而不一定等待其结束。
(5)回滚规则
事务五边形的最后一组规则,这些规则定义了那些异常会导致事务回滚而哪些不会。默认情况下,事务只有遇到运行期异常时才会回滚,而在遇到检查异常时不会回滚(这一行为与EJB的回滚行为是一只的)
但是你可以声明事务在遇到特定的检查异常时像遇到运行期异常那样回滚。同样你还可以声明事务遇到特定的异常不会滚,即使这些异常是运行期异常。
二、SpringBoot中实现事务
在SpringBoot中实现事务是比较简单的,具体列子如下:
@Transactional(rollbackFor = {Exception.class}, propagation = Propagation.REQUIRES_NEW)
@Override
public void addUsers() throws Exception {
for (int i = 0 ;i<10;i++){
User user = new User("test"+i,10+i,"test"+i+"test");
userMapper.addUser(user);
if (i == 11){
throw new Exception();
}
}
}
单元测试
@Test
public void testUser(){
try {
userServiceImpl.addUsers();
} catch (Exception e) {
e.printStackTrace();
}
}
根据上面的列子可以看出,在提交事务抛出异常后事务中的修改操作直接被回滚了。
具体的代码请参考gitHub地址:SpringBoot--transaction