Spring 事务

Spring 事务

关于理论性的内容,我在之前的一篇文章中介绍过,这里不再过多阐述,这里给出之前文章的链接:Spring 事务管理

什么是事务

是一组逻辑操作,要么执行,要么不执行。

事务的特性

ACID
(原子性、一致性、隔离性、持久性)

并发事务带来的问题

  • 脏读
  • 丢失修改
  • 不可重复读
  • 幻读

配置事务管理器

<!-- 事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
	<!-- 数据源 -->
	<property name="dataSource" ref="dataSource" />
</bean>

Spring 事务接口

  • PlatformTransactionManager:(平台)事务管理器
  • TransactionDefinition: 事务定义信息(事务隔离级别、传播行为、超时、只读、回滚规则)
  • TransactionStatus: 事务运行状态
1. PlatfromTransactionManager 接口介绍:

该接口主要有三个方法:

Public interface PlatformTransactionManager()...{

    TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException; 

    Void commit(TransactionStatus status) throws TransactionException;  

    Void rollback(TransactionStatus status) throws TransactionException;  
} 
  • getTransaction:根据指定的传播行为,返回当前活动的事务或创建一个新事务
  • commit:使用事务目前的状态提交事务
  • rollback:对执行的事务进行回滚

一般情况下用的比较多的就是 commit 提交事务和 rollback 回滚事操作了。

2. TransactiDefinition 接口介绍:

该接口主要定义了一些表示事务属性(如隔离级别,传播行为等)的常量。当然还有一些方法,仅有篇幅,不对方法做介绍。

(1)事务隔离级别(定义了一个事务可能受其他并发事务影响的程度):

TransactionDefinition 接口中定义了五个表示隔离级别的常量:

  • TransactionDefinition.ISOLATION_DEFAULT: 使用后端数据库默认的隔离级别。Mysql 默认采用 REPEATALBE-READ 隔离级别;Oracle 默认采用 READ_COMMITTED 隔离级别。
  • TransactionDefinition.ISOLATION_READ_UNCOMMITTED:最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。
  • TransactionDefinition.ISOLATION_READ_COMMITTED:允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生。
  • TransactionDefinition.ISOLATION_REPEATABLE_READ:对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
  • TransactionDefinition.ISOLATION_SERIALIZABLE:最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。
(2)事务传播行为(为了解决业务层方法之间互相调用的事务问题):

当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。在TransactionDefinition定义中包括了如下几个表示传播行为的常量:

支持当前事务的情况:

  • TransactionDefinition.PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
  • TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
  • TransactionDefinition.PROPAGATION_MANDATORY: 如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。(mandatory:强制性)

不支持当前事务的情况:

  • TransactionDefinition.PROPAGATION_REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起。
  • TransactionDefinition.PROPAGATION_NOT_SUPPORTED: 以非事务方式运行,如果当前存在事务,则把当前事务挂起。
  • TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。

其他情况:

  • TransactionDefinition.PROPAGATION_NESTED:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED
(3)TransactionStatus 接口介绍

TransactionStatus接口用来记录事务的状态 该接口定义了一组方法,用来获取或判断事务的相应状态信息。
TransactionStatus 接口方法如下:

public interface TransactionStatus{
    boolean isNewTransaction(); // 是否是新的事物
    boolean hasSavepoint(); // 是否有恢复点
    void setRollbackOnly();  // 设置为只回滚
    boolean isRollbackOnly(); // 是否为只回滚
    boolean isCompleted; // 是否已完成
} 

举例

事务操作一般都是在 service 层编写,结合增删改查方法。我举个栗子:

class Test {
    
    private PlatformTransactionManager transactionManager;
    
    public void saveFromDB() {

    	DefaultTransactionDefinition defaultTransactionDefinition = new DefaultTransactionDefinition();
    	defaultTransactionDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
    	TransactionStatus transactionStatus = transactionManager.getTransaction(defaultTransactionDefinition);

    	try {      
        	// 增删改查操作(使用 dao 中的方法)
        	//...
        	transactionManager.commit(transactionStatus);
        
        	// 删除本地缓存操作
        	//...
        
    	} catch (Exception e) {
        	transactionManager.rollback(transactionStatus);
        
    	}
	}
}

这里需要注意的是:当我们执行增删查改操作时,应该在 service 层添加事务,并且删除之前的缓存(本地缓存和 redis 缓存),然后再执行增删查改操作,这三方面都兼顾到了才是一个比较好的增删查改操作。

参考

https://juejin.im/post/5b00c52ef265da0b95276091

posted @ 2019-08-18 21:33  希希里之海  阅读(173)  评论(0编辑  收藏  举报