spring5 源码深度解析----- @Transactional注解的声明式事物介绍(100%理解事务)

面的几个章节已经分析了spring基于@AspectJ的源码,那么接下来咱们分析一下Aop的另外一个重要功能,事物管理。html

事务的介绍

1.数据库事物特性

  • 原子性
    多个数据库操做是不可分割的,只有全部的操做都执行成功,事物才能被提交;只要有一个操做执行失败,那么全部的操做都要回滚,数据库状态必须回复到操做以前的状态
  • 一致性
    事物操做成功后,数据库的状态和业务规则必须一致。例如:从A帐户转帐100元到B帐户,不管数据库操做成功失败,A和B两个帐户的存款总额是不变的。
  • 隔离性
    当并发操做时,不一样的数据库事物之间不会相互干扰(固然这个事物隔离级别也是有关系的)
  • 持久性
    事物提交成功以后,事物中的全部数据都必须持久化到数据库中。即便事物提交以后数据库马上崩溃,也须要保证数据能可以被恢复。

2.事物隔离级别

当数据库并发操做时,可能会引发脏读、不可重复读、幻读、第一类丢失更新、第二类更新丢失等现象。java

  • 脏读
    事物A读取事物B还没有提交的更改数据,并作了修改;此时若是事物B回滚,那么事物A读取到的数据是无效的,此时就发生了脏读。
  • 不可重复读
    一个事务执行相同的查询两次或两次以上,每次都获得不一样的数据。如:A事物下查询帐户余额,此时恰巧B事物给帐户里转帐100元,A事物再次查询帐户余额,那么A事物的两次查询结果是不一致的。
  • 幻读
    A事物读取B事物提交的新增数据,此时A事物将出现幻读现象。幻读与不可重复读容易混淆,如何区分呢?幻读是读取到了其余事物提交的新数据,不可重复读是读取到了已经提交事物的更改数据(修改或删除)

对于以上问题,能够有多个解决方案,设置数据库事物隔离级别就是其中的一种,数据库事物隔离级别分为四个等级,经过一个表格描述其做用。mysql

隔离级别 脏读 不可重复读 幻象读
READ UNCOMMITTED 容许 容许 容许
READ COMMITTED 脏读 容许 容许
REPEATABLE READ 不容许 不容许 容许
SERIALIZABLE 不容许 不容许 不容许

3.Spring事物支持核心接口

 
  • TransactionDefinition-->定义与spring兼容的事务属性的接口
public interface TransactionDefinition { // 若是当前没有事物,则新建一个事物;若是已经存在一个事物,则加入到这个事物中。
    int PROPAGATION_REQUIRED = 0; // 支持当前事物,若是当前没有事物,则以非事物方式执行。
    int PROPAGATION_SUPPORTS = 1; // 使用当前事物,若是当前没有事物,则抛出异常。
    int PROPAGATION_MANDATORY = 2; // 新建事物,若是当前已经存在事物,则挂起当前事物。
    int PROPAGATION_REQUIRES_NEW = 3; // 以非事物方式执行,若是当前存在事物,则挂起当前事物。
    int PROPAGATION_NOT_SUPPORTED = 4; // 以非事物方式执行,若是当前存在事物,则抛出异常。
    int PROPAGATION_NEVER = 5; // 若是当前存在事物,则在嵌套事物内执行;若是当前没有事物,则与PROPAGATION_REQUIRED传播特性相同
    int PROPAGATION_NESTED = 6; // 使用后端数据库默认的隔离级别。
    int ISOLATION_DEFAULT = -1; // READ_UNCOMMITTED 隔离级别
    int ISOLATION_READ_UNCOMMITTED = Connection.TRANSACTION_READ_UNCOMMITTED; // READ_COMMITTED 隔离级别
    int ISOLATION_READ_COMMITTED = Connection.TRANSACTION_READ_COMMITTED; // REPEATABLE_READ 隔离级别
    int ISOLATION_REPEATABLE_READ = Connection.TRANSACTION_REPEATABLE_READ; // SERIALIZABLE 隔离级别
    int ISOLATION_SERIALIZABLE = Connection.TRANSACTION_SERIALIZABLE; // 默认超时时间
    int TIMEOUT_DEFAULT = -1; // 获取事物传播特性
    int getPropagationBehavior(); // 获取事物隔离级别
    int getIsolationLevel(); // 获取事物超时时间
    int getTimeout(); // 判断事物是否可读
    boolean isReadOnly(); // 获取事物名称
 @Nullable String getName(); }
  1. Spring事物传播特性表:
传播特性名称 说明
PROPAGATION_REQUIRED 若是当前没有事物,则新建一个事物;若是已经存在一个事物,则加入到这个事物中
PROPAGATION_SUPPORTS 支持当前事物,若是当前没有事物,则以非事物方式执行
PROPAGATION_MANDATORY 使用当前事物,若是当前没有事物,则抛出异常
PROPAGATION_REQUIRES_NEW 新建事物,若是当前已经存在事物,则挂起当前事物
PROPAGATION_NOT_SUPPORTED 以非事物方式执行,若是当前存在事物,则挂起当前事物
PROPAGATION_NEVER 以非事物方式执行,若是当前存在事物,则抛出异常
PROPAGATION_NESTED

若是当前存在事物,则在嵌套事物内执行;spring

若是当前没有事物,则与PROPAGATION_REQUIRED传播特性相同sql

  1. Spring事物隔离级别表:
事务隔离级别  脏读  不可重复读  幻读
 读未提交(read-uncommitted)  是  是  是
 不可重复读(read-committed)  否   是  是
 可重复读(repeatable-read)  否   否   是
 串行化(serializable)  否   否   否 

mysql默认的事务隔离级别为 可重复读repeatable-read数据库

  3.PlatformTransactionManager-->Spring事务基础结构中的中心接口apache

public interface PlatformTransactionManager { // 根据指定的传播行为,返回当前活动的事务或建立新事务。
    TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException; // 就给定事务的状态提交给定事务。
    void commit(TransactionStatus status) throws TransactionException; // 执行给定事务的回滚。
    void rollback(TransactionStatus status) throws TransactionException; }

Spring将事物管理委托给底层的持久化框架来完成,所以,Spring为不一样的持久化框架提供了不一样的PlatformTransactionManager接口实现。列举几个Spring自带的事物管理器:编程

事物管理器 说明
org.springframework.jdbc.datasource.DataSourceTransactionManager 提供对单个javax.sql.DataSource事务管理,用于Spring JDBC抽象框架、iBATIS或MyBatis框架的事务管理
org.springframework.orm.jpa.JpaTransactionManager 提供对单个javax.persistence.EntityManagerFactory事务支持,用于集成JPA实现框架时的事务管理
org.springframework.transaction.jta.JtaTransactionManager 提供对分布式事务管理的支持,并将事务管理委托给Java EE应用服务器事务管理器

 

  • TransactionStatus-->事物状态描述
  1. TransactionStatus接口
public interface TransactionStatus extends SavepointManager, Flushable { // 返回当前事务是否为新事务(不然将参与到现有事务中,或者可能一开始就不在实际事务中运行)
    boolean isNewTransaction(); // 返回该事务是否在内部携带保存点,也就是说,已经建立为基于保存点的嵌套事务。
    boolean hasSavepoint(); // 设置事务仅回滚。
    void setRollbackOnly(); // 返回事务是否已标记为仅回滚
    boolean isRollbackOnly(); // 将会话刷新到数据存储区
 @Override void flush(); // 返回事物是否已经完成,不管提交或者回滚。
    boolean isCompleted(); }
  1. SavepointManager接口
public interface SavepointManager { // 建立一个新的保存点。
    Object createSavepoint() throws TransactionException; // 回滚到给定的保存点。 // 注意:调用此方法回滚到给定的保存点以后,不会自动释放保存点, // 能够经过调用releaseSavepoint方法释放保存点。
    void rollbackToSavepoint(Object savepoint) throws TransactionException; // 显式释放给定的保存点。(大多数事务管理器将在事务完成时自动释放保存点)
    void releaseSavepoint(Object savepoint) throws TransactionException; }

Spring编程式事物

CREATE TABLE `account` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '自增主键', `balance` int(11) DEFAULT NULL COMMENT '帐户余额', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8 COMMENT='--帐户表'
  • 实现
 1 import org.apache.commons.dbcp.BasicDataSource;  2 import org.springframework.dao.DataAccessException;  3 import org.springframework.jdbc.core.JdbcTemplate;  4 import org.springframework.jdbc.datasource.DataSourceTransactionManager;  5 import org.springframework.transaction.TransactionDefinition;  6 import org.springframework.transaction.TransactionStatus;  7 import org.springframework.transaction.support.DefaultTransactionDefinition;  8 
 9 import javax.sql.DataSource; 10 
11 /**
12  * Spring编程式事物 13  * @author: Chenhao 14  * @create: 2019-10-08 11:41 15  */
16 public class MyTransaction { 17 
18     private JdbcTemplate jdbcTemplate; 19     private DataSourceTransactionManager txManager; 20     private DefaultTransactionDefinition txDefinition; 21     private String insert_sql = "insert into account (balance) values ('100')"; 22 
23     public void save() { 24 
25         // 一、初始化jdbcTemplate
26         DataSource dataSource = getDataSource(); 27         jdbcTemplate = new JdbcTemplate(dataSource); 28 
29         // 二、建立物管理器
30         txManager = new DataSourceTransactionManager(); 31  txManager.setDataSource(dataSource); 32 
33         // 三、定义事物属性
34         txDefinition = new DefaultTransactionDefinition(); 35  txDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); 36 
37         // 三、开启事物
38         TransactionStatus txStatus = txManager.getTransaction(txDefinition); 39 
40         // 四、执行业务逻辑
41         try { 42  jdbcTemplate.execute(insert_sql); 43             //int i = 1/0;
44  jdbcTemplate.execute(insert_sql); 45  txManager.commit(txStatus); 46         } catch (DataAccessException e) { 47  txManager.rollback(txStatus); 48  e.printStackTrace(); 49  } 50 
51  } 52 
53     public DataSource getDataSource() { 54         BasicDataSource dataSource = new BasicDataSource(); 55         dataSource.setDriverClassName("com.mysql.jdbc.Driver"); 56         dataSource.setUrl("jdbc:mysql://localhost:3306/my_test?useSSL=false&useUnicode=true&characterEncoding=UTF-8"); 57         dataSource.setUsername("root"); 58         dataSource.setPassword("chenhao1991@"); 59         return dataSource; 60  } 61 
62 }
  • 测试类及结果
public class MyTest { @Test public void test1() { MyTransaction myTransaction = new MyTransaction(); myTransaction.save(); } }

运行测试类,在抛出异常以后手动回滚事物,因此数据库表中不会增长记录。后端

基于@Transactional注解的声明式事物

其底层创建在 AOP 的基础之上,对方法先后进行拦截,而后在目标方法开始以前建立或者加入一个事务,在执行完目标方法以后根据执行状况提交或者回滚事务。经过声明式事物,无需在业务逻辑代码中掺琐事务管理的代码,只需在配置文件中作相关的事务规则声明(或经过等价的基于标注的方式),即可以将事务规则应用到业务逻辑中。服务器

  • 接口
import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; /** * 帐户接口 * @author: ChenHao * @create: 2019-10-08 18:38 */ @Transactional(propagation = Propagation.REQUIRED) public interface AccountServiceImp { void save() throws RuntimeException; }
  • 实现
import org.springframework.jdbc.core.JdbcTemplate; /** * 帐户接口实现 * @author: ChenHao * @create: 2019-10-08 18:39 */
public class AccountServiceImpl implements AccountServiceImp { private JdbcTemplate jdbcTemplate; private static String insert_sql = "insert into account(balance) values (100)"; @Override public void save() throws RuntimeException { System.out.println("==开始执行sql"); jdbcTemplate.update(insert_sql); System.out.println("==结束执行sql"); System.out.println("==准备抛出异常"); throw new RuntimeException("==手动抛出一个异常"); } public void setJdbcTemplate(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } }
  • 配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd">

    <!--开启tx注解-->
    <tx:annotation-driven transaction-manager="transactionManager"/>

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

    <!--数据源-->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/my_test?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
        <property name="username" value="root"/>
        <property name="password" value="chenhao1991@"/>
    </bean>

    <!--jdbcTemplate-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!--业务bean-->
    <bean id="accountService" class="com.chenhao.aop.AccountServiceImpl">
        <property name="jdbcTemplate" ref="jdbcTemplate"/>
    </bean>

</beans>
  • 测试
import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * @author: ChenHao * @create: 2019-10-08 18:45 */
public class MyTest { @Test public void test1() { // 基于tx标签的声明式事物
        ApplicationContext ctx = new ClassPathXmlApplicationContext("aop.xml"); AccountServiceImp studentService = ctx.getBean("accountService", AccountServiceImp.class); studentService.save(); } }
  • 测试
==开始执行sql ==结束执行sql ==准备抛出异常 java.lang.RuntimeException: ==手动抛出一个异常 at com.lyc.cn.v2.day09.AccountServiceImpl.save(AccountServiceImpl.java:24) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498)

测试方法中手动抛出了一个异常,Spring会自动回滚事物,查看数据库能够看到并无新增记录。

注意:默认状况下Spring中的事务处理只对RuntimeException方法进行回滚,因此,若是此处将RuntimeException替换成普通的Exception不会产生回滚效果。

接下来咱们就分析基于@Transactional注解的声明式事物的的源码实现。

 
 

 

原文出处:https://www.cnblogs.com/java-chen-hao/p/11635525.html

相关文章
相关标签/搜索