事务特性

确保数据的完整性和一致性

  • 原子性(Atomicity):事务是一个原子操作,由一系列动作组成.事务的原子性确保动作要么全部完成,要么完全不起作用.
  • 一致性(Consistency):一旦事务完成(不管成功还是失败),系统必须确保它所建模的业务处于一致的状态,而不会是部分完成部分失败.在现实中的数据不应该被破坏.
  • 隔离性(Isolation):可能有许多事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏.
  • 持久性(Durability):一旦事务完成,无论发生什么系统错误,它的结果都不应该受到影响,这样就能从任何系统崩溃中恢复过来.通常情况下,事务的结果被写到持久化存储器中.

Spring 事务的隔离级别

TransactionDefinition接口中定义了五个不同的事务隔离级别

  • ISOLATION_DEFAULT
    这是一个 PlatfromTransactionManager 默认的隔离级别,使用数据库默认的事务隔离级别.另外四个与 JDBC 的隔离级别相对应
  • ISOLATION_READ_UNCOMMITTED
    这是事务最低的隔离级别,它允许另外一个事务可以看到这个事务未提交的数据.这种隔离级别会产生脏读,不可重复读和幻像读
  • ISOLATION_READ_COMMITTED
    保证一个事务修改的数据提交后才能被另外一个事务读取.另外一个事务不能读取该事务未提交的数据.这种事务隔离级别可以避免脏读出现,但是可能会出现不可重复读和幻像读
  • ISOLATION_REPEATABLE_READ
    这种事务隔离级别可以防止脏读/不可重复读.但是可能出现幻像读.它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了避免不可重复读
  • ISOLATION_SERIALIZABLE
    这是花费最高代价但是最可靠的事务隔离级别,事务被处理为顺序执行.除了防止脏读,不可重复读外,还避免了幻像读

Spring 事务的传播属性

TransactionDefinition接口中定义了七个事务传播行为

  • PROPAGATION_REQUIRED
    如果存在一个事务,则支持当前事务.如果没有事务则开启一个新的事务.
  • PROPAGATION_SUPPORTS
    如果存在一个事务,支持当前事务.如果没有事务,则非事务的执行.但是对于事务同步的事务管理器,PROPAGATION_SUPPORTS与不使用事务有少许不同.
  • PROPAGATION_MANDATORY
    如果已经存在一个事务,支持当前事务.如果没有一个活动的事务,则抛出异常.
  • PROPAGATION_REQUIRES_NEW
    总是开启一个新的事务.如果一个事务已经存在,则将这个存在的事务挂起.
  • PROPAGATION_NOT_SUPPORTED
    总是非事务地执行,并挂起任何存在的事务.
  • PROPAGATION_NEVER
    总是非事务地执行,如果存在一个活动事务,则抛出异常
  • PROPAGATION_NESTED
    如果一个活动的事务存在,则运行在一个嵌套的事务中.如果没有活动事务, 则按TransactionDefinition.PROPAGATION_REQUIRED属性执行

@Transactiona 注解

@Transactional 默认情况下会对运行期例外(RunTimeException)进行事务回滚.这个例外是 unchecked

  • checked 例外也回滚:在方法前加上 @Transactional(rollbackFor=Exception.class)

  • unchecked 例外不回滚:在方法前加上 @Transactional(notRollbackFor=RunTimeException.class)

  • 不需要事务管理的(只查询的)方法:@Transactional(propagation=Propagation.NOT_SUPPORTED)

  • 还可以加上:@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true),这样就做成一个只读事务可以提高效率

注意 如下代码片段 d1 调用 d2 方法,其中 d1 中 userDao.insert(userPO) 执行成功 d2 中 userDao.insert2(userPO) 执行失败

  • 情况一:
    d2 没有@Transactional 注解 (标记 3)
    d1 catch 没有 throw 异常 (标记 2)
    这时 userDao.insert 不会 rollback
    控制台没有异常信息
  • 情况二:
    d2 @Transactional 注解 (标记 3)
    d1 catch 没有 throw 异常 (标记 2)
    这时 userDao.insert 会 rollback
    控制台异常信息: org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only

原因:
情况二 中 spring 切面在 在执行 d2 后 优先 catch 住了异常 并进行 throw 所以整个方法回滚,
而在情况一 中 d2 异常但是 没有抛出异常 所以不会触发 rollback

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@Service
public class TestService {
@Autowired
private UserDao userDao;

@Autowired
private TestService testService;

@Transactional(rollbackFor = {Exception.class})
public void d1() throws Exception {
UserPO userPO = new UserPO();
userPO.setName("u1");
userDao.insert(userPO); //success
try {
testService.d2();
} catch (Exception e) {
System.out.println("error");
// throw new Exception(); // ---> 2
}
}

// @Transactional(rollbackFor = {Throwable.class}) // ---> 3
public void d2() {
UserPO userPO = new UserPO();
userPO.setName("u2");
userDao.insert2(userPO); // error
}
}