美文网首页
Spring事务管理

Spring事务管理

作者: sunhaiyu | 来源:发表于2018-10-11 16:13 被阅读12次

Spring事务管理

什么是事务?

事务指的是逻辑上的一组操作,这组操作要么全部成功,要么全部失败。

事务的四大特性(ACID)

  • 原子性(Atomicity):事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
  • 一致性(Consistency):事务开始前和结束后,数据的完整性约束没有被破环。比如A向B转了钱,转账前后钱的总数不变。
  • 隔离性(Isolation):多个用户并发访问数据数据库时,一个用户的事务不能被其他用户的事务所干扰,多个并发事务之间的数据相互隔离。比如事务A和事务B都修改同一条记录,这条记录就会被重复修改或者后者会覆盖前者的修改记录。
  • 持久性(Durability):事务完成后,事务对数据库的更新被保存到数据库,其结果是永久的。

事务并发可能产生的问题

脏数据:事务对缓冲池中的行记录进行修改,但是还没有被提交。

  • 脏读:事务A读取到了事务B修改但未提交的数据。如果此时B回滚到修改之前的状态,A就读到了脏数据。
  • 不可重复读:事务A多次读取同一个数据,此时事务B在A读取过程中对数据修改并提交了,导致事务A在同一个事务中多次读取同一数据而结果不同。
  • 幻读:事务A对表进行修改,这个修改涉及到表中所有的行,但此时事务B新插入了一条数据,事务A就会发现居然还有数据没有被修改,就好像发生幻觉一样。

事务隔离级别

TransactionDefinition接口中定义了事务的隔离级别和传播行为等参数。

为了应对不同的事务问题,有了如下几种事务隔离级别。

隔离级别 含义
DEFAULT 使用后端数据库默认的隔离级别
READ_UNCOMMITTED 允许读取未提交的数据,可能导致脏读、不可重复读、幻读
READ_COMMITTED 允许并发事务提交后读取,可能导致不可重复读、幻读。
REPEATABLE_READ 在同一次事务中对相同字段的多次读取结果一致,除非数据本身被改变。可防止脏读和不可重复读,但依然存在幻读的可能
SERIALIZABLE 不会发生 脏读、不可重复读、幻读等问题。隔离级别最高,但是并发下效率低,一般是通过锁表来实现的。

事务的传播行为

推荐阅读这篇博客,以下关于事务传播行为的内容均出自于该篇博客。

事务传播行为类型 说明
REQUIRED 如果已经存在一个事务中,加入到这个事务中;如果当前没有事务,就新建一个事务。Spring的默认传播行为
SUPPORTS 如果已经存在一个事务中,加入到这个事务中;如果当前没有事务,就以非事务方式执行。
MANDATORY 如果已经存在一个事务中,加入到这个事务中;如果当前没有事务,就抛出异常
REQUIRES_NEW 如果当前存事务,将当前事务挂起不使用,新建一个事务
NOT_SUPPORTED 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
NEVER 以非事务方式执行,如果当前存在事务,则抛出异常。
NESTED 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。

当使用PROPAGATION_NESTED时,底层的数据源必须基于JDBC 3.0,并且实现者需要支持保存点事务机制。有时候业务比较复杂,经常可能有service之间互相调用的情况发生,比如下面:

ForumSerivce#addTopic()方法调用了 UserSerice#addCredits()方法,发生关联性服务方法的调用:

public class ForumService {

private UserService userService;

public void addTopic() {

    // 被关联调用的业务方法
    userService.addCredits();
    
}

}

当事务隔离级别是REQUIRED、SUPPORTS、MANDATORY时,都是将事务加入到已经存在的事务中。因此将ForumService#addTopic()设置为REQUIRED时, UserSerice#addCredits()设置为REQUIRED、SUPPORTS、 MANDATORY时,运行的效果都是一致的。

当addTopic()运行在一个事务下(如设置为REQUIRED),而addCredits()设置为NESTED时,如果底层数据源支持保存点,Spring将为内部的addCredits()方法产生的一个内嵌的事务。如果 addCredits()对应的内嵌事务执行失败,事务将回滚到addCredits()方法执行前的点,并不会将整个事务回滚。内嵌事务是内层事务的一 部分,所以只有外层事务提交时,嵌套事务才能一并提交。

嵌套事务不能够提交,它必须通过外层事务来完成提交的动作,外层事务的回滚也会造成内部事务的回滚。

REQUIRES_NEW 和NESTED也是容易混淆的两个传播行为。

REQUIRES_NEW启动一个新的、和外层事务无关的“内部”事务。该事务拥有自己的独立隔离级别,不依赖于外部事务,独立地提交和回滚。当内部事务开始执行时,外部事务 将被挂起,内务事务结束时,外部事务才继续执行。

由此可见,NEW 和 NESTED 的最大区别在于:REQUIRES_NEW 将创建一个全新的事务,它和外层事务没有任何关系,而NESTED 将创建一个依赖于外层事务的子事务,当外层事务提交或回滚时,子事务也会连带提交和回滚。

事务的状态

TransactionStatus接口中有一些方法可以查询事务的状态。

// 该事务是否是一个新事务
boolean isNewTransaction();
// 该事务是否有保存点
boolean hasSavepoint();
// 设置仅事务回滚。这指示事务管理的唯一可能结果是回滚,作为抛出异常的替代方法,而异常又会触发回滚。
void setRollbackOnly();
// 是否被设置了rollback-only
boolean isRollbackOnly();
// 将潜在的会话(underlying session)刷新到数据库中
void flush();
// 事务是否已经完成
boolean isCompleted();

PlatformTransactionManager

PlatformTransactionManager(平台事务管理器)

Spring为不同的持久化框架提供了不同的PlatformTransactionManager接口实现。

image

事务的使用

在Spring Boot中,声明事务很简单。只需要在service层的类或者方法上加上@Transactional注解即可,省去了繁琐的配置。在service类上加@Transactional,声明这个service所有方法需要事务管理。每一个业务方法开始时都会打开一个事务。

@Transactional
public void transfer(AccountA a, AccountB a, Double money) {
    a.subtract(100.0);
    b.add(100.0);
}

@Transactional可以配置如下属性

  • transactionManager,可以指定具体的PlatformTransactionManager
  • propagation,传播行为,默认是REQUIRED
  • isolation,隔离级别,默认是DEFAULT(使用后端数据库默认的隔离级别)
  • timeout,设置超时时间(秒),超时后会回滚事务。默认值-1。
  • readOnly,该属性用于设置当前事务是否为只读事务,默认值为false。
  • rollbackFor,该属性用于设置需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,则进行事务回滚。
  • noRollbackFor,该属性用于设置不需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,不进行事务回滚
  • rollbackForClassName,该属性用于设置需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,则进行事务回滚。
  • noRollbackForClassNam,该属性用于设置不需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,不进行事务回滚

Spring默认情况下会对运行时异常(RunTimeException)进行事务回滚,这些异常属于非受检异常。比如ArrayIndexOutOfBoundsException,NullPointerException;但不会对受检异常进行事务回滚,这些异常包括:ClassNotFoundException、NoSuchMetodException等。若要对所有异常都进行事务回滚,可以如下设置。

@Transactional(rollbackFor=Exception.class)

相关文章

网友评论

      本文标题:Spring事务管理

      本文链接:https://www.haomeiwen.com/subject/mbibaftx.html