为了账号安全,请及时绑定邮箱和手机立即绑定

Spring5.0源码学习系列之事务管理概述

标签:
Spring

Spring源码学习系列博客专栏:链接

Spring5.0源码学习系列之事务管理概述(十一),在学习事务管理的源码之前,需要对事务的基本理论比较熟悉,所以本章节会对事务管理的基本理论进行描述

1、什么是事务?

事务就是一组原子性的SQL操作,或者说一个独立的工作单元。在计算机术语中是指访问并可能更新数据库中各种数据项的一个程序执行单元(unit)

注意:Spring的事务支持是基于数据库事务的,在MySQL数据库中目前只有InnoDB或者NDB集群引擎才支持,MySQL5.0之前的默认MyISAM存储引擎是不支持事务的

2、事务的ACID特性

ACID其实是事务特性的英文首字母缩写,具体含义是:原子性(atomicity)、一致性(consistency)、隔离性(isolation)、持久性(durability)

  • 原子性(atomicity):事务是一个原子操作,由一系列动作组成。整个事务中的所有操作要么全部提交成功,要么全部失败回滚;
  • 一致性(consistency):数据库总是从一个一致性的状态转换到另外一个一致性的状态,执行事务前后,数据保持一致;
  • 隔离性(isolation): 因为有多个事务处理同个数据的情况,因此每个事务都应该与其他事务隔离开来,防止数据脏读、不可重复读等等情况;
  • 持久性(durability):一旦事务提交,则其所做的修改就会永久保存到数据库中。此时即使系统崩溃,修改的数据也不会丢;

3、什么是脏读、不可重复读、幻读?

  • 脏读
    在A事务修改数据,提交事务之前,另外一个B事务读取了A事务未提交事务之前的数据,这种情况称之为脏读(Dirty Read)
  • 不可重复读
    一个A事务在读取某些数据,第1次读取出来的数据结果和第2次读取出来的不一致,因为在两次数据读取期间,另外的事务对数据进行了更改
  • 幻读
    幻读和不可重复读是很类似的,不同的地方在于幻读侧重于事务对数据的删除或者新增,都是因为在两次数据读取期间,因为另外事务对数据的删除还是新增,导致第2次读取的数据和第1次不一致

4、Spring事务管理核心接口

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

5、事务隔离级别

定义:事务的隔离级别定义了一个事务可能受其他并发事务影响的程度。隔离级别可以不同程度的解决脏读、不可重复读、幻读。

图片描述

6、事务的传播行为

图片描述

7、事务管理其它属性

前面介绍了事务管理的隔离级别和传播行为这两个重要的属性,接着介绍一下事务的其它属性

  • 事务超时属性
    事务超时,属性值是timeout,指一个事务所允许执行的最长时间,如果超过该时间限制但事务还没有完成,则自动回滚事务。以 int 的值来表示超时时间,其单位是秒,默认值为-1。
  • 事务只读属性
    属性值readOnly,对于只有读取数据查询的事务,可以指定事务类型为 readonly,即只读事务。只读事务不涉及数据的修改,数据库会提供一些优化手段,所以对于业务很明确的接口,可以适当加上只读属性
  • 事务回滚规则
    属性值rollbackFor,默认情况下,事务只有遇到运行期异常(RuntimeException 的子类)时才会回滚,Error 也会导致事务回滚

图片描述

8、Spring事务实现方式

Spring事务代码实现方式有两种,一种是编程式事务,一种是声明式事务。所谓编程式事务,是指通过Spring框架提供的TransactionTemplate或者直接使用底层的PlatformTransactionManager。声明式事务,依赖Spring AOP,配置文件中做相关的事务规则声明或者直接使用@Transactional注解

下面给出一个典型的转账汇款例子,先不用事务的方式实现,接着使用编程式事务和声明式事务进行事务管理

package com.example.springframework.dao.impl;

import com.example.springframework.dao.AccountDao;
import org.springframework.jdbc.core.support.JdbcDaoSupport;

/**
 * <pre>
 *      AccountDaoImpl
 * </pre>
 *
 * <pre>
 * @author mazq
 * 修改记录
 *    修改后版本:     修改人:  修改日期: 2021/03/25 15:51  修改内容:
 * </pre>
 */
public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {

    @Override
    public void out(String outer, int money) {
        super.getJdbcTemplate().update("update account set money = money - ? where usercode=?",money,outer);
    }

    @Override
    public void in(String inner, int money) {
        super.getJdbcTemplate().update("update account set money = money + ? where usercode = ?",money , inner);
    }
}


package com.example.springframework.service.impl;

import com.example.springframework.dao.AccountDao;
import com.example.springframework.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import org.springframework.stereotype.Service;


/**
 * <pre>
 *      AccountServiceImpl
 * </pre>
 *
 * <pre>
 * @author mazq
 * 修改记录
 *    修改后版本:     修改人:  修改日期: 2021/03/25 15:55  修改内容:
 * </pre>
 */
@Service
public class AccountServiceImpl extends JdbcDaoSupport implements AccountService {

    AccountDao accountDao;

    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }

    public void transfer(final String outer,final String inner,final int money){
        accountDao.out(outer , money);
        // exception
        // int i = 1 / 0;
        accountDao.in(inner , money);
    }
    
}

代码例子看起来是挺正常的,不过假如在accountDao.out(outer , money);accountDao.in(inner , money);两个事务执行期间,发生异常,这时会怎么样?效果如图:Jack的账号已经转账成功,转了1000,不过Tom并没有收到汇款,这种情况在实际生活中肯定是不允许的,所以需要使用事务进行管理
在这里插入图片描述

9、Spring编程式事务

Spring编程式事务实现,通过Spring框架提供的TransactionTemplate或者直接使用底层的PlatformTransactionManager

  • 使用TransactionTemplate的方式
private AccountDao accountDao;
private TransactionTemplate transactionTemplate;

public void setAccountDao(AccountDao accountDao) {
    this.accountDao = accountDao;
}
public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
    this.transactionTemplate = transactionTemplate;
}

@Override
public void transfer(final String outer,final String inner,final int money){
    transactionTemplate.execute(new TransactionCallbackWithoutResult() {
        @Override
        protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
            accountDao.out(outer , money);
            // exception
            int i = 1 / 0;
            accountDao.in(inner , money);
        }
    });
}

  • 使用PlatformTransactionManager的方式
private AccountDao accountDao;

public void setAccountDao(AccountDao accountDao) {
    this.accountDao = accountDao;
}

@Override
public void transferTrans(String outer, String inner, int money) {
    DataSourceTransactionManager dataSourceTransactionManager =
            new DataSourceTransactionManager();
    // 设置数据源
    dataSourceTransactionManager.setDataSource(super.getJdbcTemplate().getDataSource());
    DefaultTransactionDefinition transDef = new DefaultTransactionDefinition();
    // 设置传播行为属性
    transDef.setPropagationBehavior(DefaultTransactionDefinition.PROPAGATION_REQUIRED);
    TransactionStatus status = dataSourceTransactionManager.getTransaction(transDef);
    try {
        accountDao.out(outer , money);
        // exception
        int i = 1 / 0;
        accountDao.in(inner , money);
        //commit
        dataSourceTransactionManager.commit(status);
    } catch (Exception e) {
        // rollback
        dataSourceTransactionManager.rollback(status);
    }
}

10、Spring声明式事务

Spring声明式事务依赖于Spring AOP,通过配置文件中做相关的事务规则声明或者直接使用@Transactional注解

  • AOP规则声明方式
    这种方式在 applicationContext.xml 文件中配置 aop 自动生成代理,进行事务管理
<?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"
       xmlns:aop="http://www.springframework.org/schema/aop"
       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 http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">

    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://127.0.0.1:3306/test"></property>
        <property name="username" value="root"></property>
        <property name="password" value="minstone"></property>
    </bean>

    <bean id="accountDao" class="com.example.springframework.dao.impl.AccountDaoImpl">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <bean id="accountService" class="com.example.springframework.service.impl.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"></property>
        <property name="dataSource" ref="dataSource"></property>
    </bean>
	<!-- 事务管理器配置 -->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!-- xml配置事务 propagation 传播行为isolation 隔离级别-->
    <tx:advice id="txAdvice" transaction-manager="txManager">
        <tx:attributes>
            <tx:method name="transfer" propagation="REQUIRED" isolation="DEFAULT"/>
        </tx:attributes>
    </tx:advice>
	
	<!-- 配置所有的Service方法都支持事务-->
    <aop:config>
     <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.example.springframework.service..*.*(..))"/>
 </aop:config>

  
</beans>

  • 使用AOP注解方式
    在applicationContext.xml 配置事务管理器,将并事务管理器交予spring,在目标类或目标方法添加注解即可 @Transactional, proxy-target-class设置为 true : 底层强制使用cglib 代理

注意点:@Transactional只能用于public方法,不管是加上类上还是方法上

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

<!-- 配置 Annotation 驱动,扫描@Transactional注解的类定义事务  -->
<tx:annotation-driven transaction-manager="txManager" proxy-target-class="true"/>

通过上述管理之后,一旦发生异常,两边都会进行事务回滚,没有异常,正常提交事务
在这里插入图片描述

本文例子代码可以在github找到下载链接

作者: smileNicky
出处:https://www.cnblogs.com/mzq123/p/14820074.html

点击查看更多内容
1人点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
Web前端工程师
手记
粉丝
14
获赞与收藏
47

关注作者,订阅最新文章

阅读免费教程

感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消