Spring 声明式事务的支持

Spring 声明式事务的支持

  • 编程式事务 :在业务代码中添加事务控制代码,这样的事务控制机制就叫做编程式事务
  • 声明式事务 :通过xml或者注解配置的方式达到事务控制的目的,叫做声明式事务

事务回顾

事务的概念

事务是指逻辑上的一组操作,组成这组操作的各个单元,要么全部成功,要么全部失败,从而确保了数据的准确和安全

例如:A ——> B 转账,对应的SQL语句

# 转出账号扣钱
update account set money = (money - 100) where name = 'A';
# 转入账号加钱
update account set money = (money + 100) where name = 'B';

这两句语句的执行,要么全部成功,要么全部不成功

事务的四大特性ACID

  • 原子性(Atomicity)

    原子性指是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。

    从操作的角度来描述,事务中的各个操作要么都成功,要么都失败。

  • 一致性(Consistency)

    事务必须使数据库从一个一致性状态变换到另一个一致性状态。

    例如转账前A有1000,B有1000,转账后A+B还得是2000。

    一致性是从数据的角度来说的,(1000,1000)=> (900,1100),不应该出现(900,1000)

  • 隔离性(Isolation)

    事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,每个事务不能被其他事务的操作数据所干扰,多个并发事务之间要互相隔离。

    比如:事务1: A余额有1000,B给A转账1000,但是事务并没有提交,事务2发起查询余额,发现余额已经是2000了,读到了事务1中尚未提交的数据(脏读)

  • 持久性(Durability)

    持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不因该对其有任何影响。

事务的隔离级别

不考虑隔离级别,会出现以下情况(以下情况全是错误的),也即为隔离级别在解决事务并发问题:

  • 脏读

    一个线程中的事务读到了另一个线程中未提交的事务

    场景:如上面隔离性的例子

  • 不可重复读

    一个线程中的事务读到了另一个线程中已经提交的update数据(前后数据不一样)

    场景:

    A余额有1000,事务1发起查询,得到余额1000,此时事务1并未关闭;事务2修改了A的余额为900,提交了事务,事务1再次发起查询,得到余额900,与第一次查询的1000不一样,原来读出来的1000读不到了,叫做不可重复读

  • 幻读

    一个线程中的事务读到了另一个线程中已经提交的insert或者delete的数据(前后数据条数不一样)

    场景:

    事务1查询余额为1000的人,总数有10个,此时事务尚未关闭

    事务2新增了2个余额为1000的人,并且提交了事务

    事务1再次查询余额为1000的人,总数有12个,跟之前查出来的条数不一致

数据库定义了四种隔离级别,从隔离级别的低到高:

  • 读未提交(Read umcommmitted)

    最低级别,无法保证脏读、不可重复读、幻读的情况

  • 读提交(Read committed)

    可避免脏读的情况发生,不可重复读和幻读一定会发生

  • 可重复读(Repeatable read)

    可避免脏读、不可重复读发生,幻读有可能发生

  • 串行化(Serializable)

    可避免以上所有情况

注意:隔离的越严实,效率就越低

MySQL的默认隔离级别是可重复读(Repeatable read)

mysql> select @@tx_isolation;
+-----------------+
| @@tx_isolation  |
+-----------------+
| REPEATABLE-READ |
+-----------------+

设置MySQL事务的隔离级别:(设置的是当前MySQL连接会话,并不是永久改变的)

set session transaction isolation level;

Spring事务的7种传播行为

事务往往在service层进行控制,如果service层的一个方法A调用了另一个方法B,这两个方法本身都已经添加了事务控制,那么A调用B时,就需要进行事务的一些协商,这叫做事务的传播行为

A调用B,我们站在B的角度来观察和定义事务的传播行为:

事务行为说明
PROPAGATION_REQUIRED表示当前方法必须运行在事务中。如果已经存在一个事务,方法将会在该事务中运行。否则,会启动一个新的事务。(增删改最常见的选择)
PROPAGATION_SUPPORTS表示当前方法不需要事务上下文,但是如果存在当前事务的话,那么该方法会在这个事务中运行。(查询最常见的选择)
PROPAGATION_MANDATORY表示该方法必须在事务中运行,如果当前事务不存在,则会抛出一个异常
PROPAGATION_REQUIRES_NEW表示当前方法必须运行在它自己的事务中,会新建一个事务,假设当前存在事务。把当前事务挂起
PROPAGATION_NOT_SUPPORTED表示该方法不应该运行在事务中。如果存在当前事务,在该方法运行期间,当前事务将被挂起。如果使用
PROPAGATION_NEVER以非事务方式运行,假设当前存在事务,则抛出异常
PROPAGATION_NESTED表示如果当前已经存在一个事务,那么该方法将会在嵌套事务中运行。嵌套的事务可以独立于当前事务进行单独地提交或回滚。如果当前事务不存在,那么其行为与PROPAGATION_REQUIRED一样。

Spring 中事务的API

org.springframework.transaction.PlatformTransactionManager 事务的根接口,提供了以下方法:

public interface PlatformTransactionManager extends TransactionManager {

	/**
	 * 获取事务状态信息
    */
	TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
			throws TransactionException;
	
    /**
     * 提交事务
     */
	void commit(TransactionStatus status) throws TransactionException;

    /**
     * 回滚事务
     */
	void rollback(TransactionStatus status) throws TransactionException;

}

此接口是Spring的事务管理器的核心接口。Spring本身并不支持事务实现,只是负责提供标准,应用底层支持什么样的事务,需要提供具体实现类。在Spring框架中,也为我们内置了一些策略,例如:DataSourceTransactionManagerHibernateTransactionManager 等。

如果持久层框架使用的是 SpringJdbcTemplate / MyBatis(mybatis-spring.jar) 则可以使用DataSourceTransactionManagerHibernate 则使用 HibernateTransactionManager (在spring-orm-5.1.12.RELEASE.jar 中)

Spring 声明式事务配置

纯xml模式

  • 导入依赖

    <!-- 引入spring ioc容器功能-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.2.5.RELEASE</version>
    </dependency>
    <!-- 引入AOP -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
        <version>5.2.5.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.9.5</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>5.2.5.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-tx</artifactId>
        <version>5.2.5.RELEASE</version>
    </dependency>
    
  • applicationContext.xml配置

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xmlns:tx="http://www.springframework.org/schema/tx"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
                               https://www.springframework.org/schema/beans/spring-beans.xsd
                               http://www.springframework.org/schema/context
                               https://www.springframework.org/schema/context/spring-context.xsd
                               http://www.springframework.org/schema/aop
                               https://www.springframework.org/schema/aop/spring-aop.xsd
                               http://www.springframework.org/schema/tx
                               https://www.springframework.org/schema/tx/spring-tx.xsd">
    
        ...
    
        <!-- spring声明式事务配置,声明式事务无非就是配置一个AOP 只不过有一些标签不一样罢了 -->
    
        <!-- spring提供的事务管理器 -->
        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"/>
    
    
        <tx:advice id="txAdvice" transaction-manager="transactionManager">
            <!--定制事务细节,传播⾏为、隔离级别等-->
            <tx:attributes>
                <!--⼀般性配置-->
                <tx:method name="*" read-only="false"
                           propagation="REQUIRED" isolation="DEFAULT" timeout="-1"/>
                <!--针对查询的覆盖性配置-->
                <tx:method name="query*" read-only="true"
                           propagation="SUPPORTS"/>
            </tx:attributes>
        </tx:advice>
    
        <aop:config>
            <!--advice-ref指向增强=横切逻辑+⽅位-->
            <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.test.service.impl.TransferServiceImpl.*(..))"/>
        </aop:config>
        
    </beans>
        
    

基于xml+注解

  • xml配置

    <!--配置事务管理器-->
    <bean id="transactionManager"
          class="org.springframework.jdbc.datasource.DataSourceTransactionManage
                 r">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    
    <!--开启spring对注解事务的⽀持-->
    <tx:annotation-driven transaction-manager="transactionManager"/>    
    
  • 在接口、类或者方法上添加**@Transactional** 注解

    @Transactional(readOnly = true,propagation = Propagation.SUPPORTS)
    

基于纯注解

基于上面的xml+注解方式,只需把xml的配置删掉

<!--开启spring对注解事务的⽀持-->
<tx:annotation-driven transaction-manager="transactionManager"/> 

然后在Spring的配置类上添加 @EnableTransactionManagement 即可

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值