九、Spring 声明式事务学习总结

本文详细介绍了声明式事务的概念、应用场景、ACID特性,以及在Spring和MyBatis项目中的应用,包括未使用事务的代码示例和配置Spring声明式事务的过程,确保数据操作的一致性。
摘要由CSDN通过智能技术生成



一、声明式事务


1.1 什么是事务

  • 把一组业务当成一个业务来做;要么都成功,要么都失败!

    • 事务在项目开发中,十分的重要,涉及到数据的一致性问题,不能马虎!


1.2 事务的应用场景

  • 在我们日常开发中,具体的业务大多数都是操作数据库的增删改查,其中包含了很多原子性的数据库操作。当同一个业务中出现多个原子性的数据库操作时,为了数据的安全性,避免数据不同步的状况发生就需要用到事务。

    • 举个栗子,银行转账

      • 两个人来到银行进行转账的操作,A 需要转给 B 1000块钱,也就是 A 的钱减少一千,B 的钱增加一千。但是如果在转账途中,出现银行系统崩溃或者网络故障等状况导致 A 的钱确实是少了一千,但是 B 的钱并没有增加,这显然是不对的。这时候事务的功效就显示出来了,两个操作要么就都成功,要么直接事务回滚,让操作都失败,以此来保证数据的安全性和一致性。



1.3 事务的特性(ACID)

  • 原子性(Atomicity):事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用;

  • 一致性(Consistency):执行事务前后,数据保持一致,例如转账业务中,无论事务是否成功,转账者和收款人的总额应该是不变的;

  • 隔离性(Isolation):并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的;

  • 持久性(Durability):一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响。

只有保证了事务的持久性、原子性、隔离性之后,一致性才能得到保障。也就是说 A、I、D 是手段,C 是目的!



1.4 未使用事务的代码示例

  • 项目背景 Spring + Mybatis ,将第八章的项目原封不动的粘过来,修改 mapper.xml ,将删除 SQL 改成错误的写法其他内容不变

  • 修改测试类(新增一条数据,在将这条数据删除)

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations = "classpath:spring-config.xml")
    public class Test_SpringMyBatis {
    
        @Autowired
        private UsersMapper userMapper;
    
        @Test
        public void testFindUserList(){
            Users users = new Users();
            users.setId("4");
            users.setName("姚青");
            users.setPwd("123456");
            int i = userMapper.saveUser(users);
            int i3 = userMapper.deleteUsers(users);
    
        }
    
    }
    
  • 执行结果,可以看出执行到 delete 方法时候报错了,但是第一条新增添加成功了,这显然不是很合理
    在这里插入图片描述

在这里插入图片描述



1.5 配置 Spring 声明式事务学习总结

  • 上面的代码执行出来的结果明显是不符合真实情况的,所有这里需要配置 Spring 的声明式事务

    • 在 Spring 配置文件中新增配置

      • 使用Spring管理事务,需要导入头文件的约束 : tx

        xmlns:tx="http://www.springframework.org/schema/tx"
        
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd">
        
      • 配置 Spring 声明式事务

         <!-- 配置 Spring 声明式事务 -->
            <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
                <constructor-arg ref="dataSource"/>
            </bean>
        
      • 配置好声明式事务之后,结合 Spring aop 实现事务的织入(这里需要引入 tx (事务))

        <!-- 结合 Spring aop 实现事务的织入 -->
        <!-- 第一步:配置事务通知 -->
        <tx:advice id="txAdvice" transaction-manager="transactionManager">
            <!-- 给那些方法配置事务,可以是具体的方法名,也可以是*,*代表所有方法 -->
            <!--配置事务的传播特性: new propagation-->
            <tx:attributes>
                <tx:method name="add" propagation="REQUIRED"/>
                <tx:method name="delete" propagation="REQUIRED"/>
                <tx:method name="update" propagation="REQUIRED"/>
                <tx:method name="query" read-only="true"/>
                <tx:method name="*" propagation="REQUIRED"/>
            </tx:attributes>
        </tx:advice>
        
        <!-- 第二步:配置事务切入 -->
        <aop:config>
            <!-- 指定切入点为 mapper 包下所有接口中的所有方法 -->
            <aop:pointcut id="txPointCut" expression="execution(* com.sys.mapper.*.*(..))"/>
            <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
        </aop:config>
        
    • Spring 完整配置

      <?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:context="http://www.springframework.org/schema/context"
             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/context
              http://www.springframework.org/schema/context/spring-context.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">
      
          <!-- 加载数据库连接信息的属性文件 -->
          <context:property-placeholder location="classpath:jdbc-config.properties"/>
      
          <!-- 配置Druid数据源的Bean -->
          <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
              <property name="driverClassName" value="${jdbc.driver}"/>
              <property name="url" value="${jdbc.url}"/>
              <property name="username" value="${jdbc.username}"/>
              <property name="password" value="${jdbc.password}"/>
          </bean>
      
          <!-- 配置SessionFactory的Bean -->
          <bean id="sessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
              <!-- 注入数据源 -->
              <property name="dataSource" ref="dataSource"/>
              <!-- 指定MyBatis配置文件的位置 -->
              <property name="configLocation" value="classpath:mybatis-config.xml"/>
              <!-- 配置 xml 的映射 -->
              <property name="mapperLocations" value="classpath:mapper/*.xml"/>
          </bean>
      
          <!-- 配置mapper接口的扫描器,将Mapper接口的实现类自动注入到IoC容器中
           实现类Bean的名称默认为接口类名的首字母小写 -->
          <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
              <!-- basePackage属性指定自动扫描mapper接口所在的包 -->
              <property name="basePackage" value="com.sys.mapper"/>
          </bean>
      
          <!-- 配置 Spring 声明式事务 -->
          <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
              <constructor-arg ref="dataSource"/>
          </bean>
      
          <!-- 结合 Spring aop 实现事务的织入 -->
          <!-- 第一步:配置事务通知 -->
          <tx:advice id="txAdvice" transaction-manager="transactionManager">
              <!-- 给那些方法配置事务,可以是具体的方法名,也可以是*,*代表所有方法 -->
              <!--配置事务的传播特性: new propagation-->
              <tx:attributes>
                  <tx:method name="add" propagation="REQUIRED"/>
                  <tx:method name="delete" propagation="REQUIRED"/>
                  <tx:method name="update" propagation="REQUIRED"/>
                  <tx:method name="query" read-only="true"/>
                  <tx:method name="*" propagation="REQUIRED"/>
              </tx:attributes>
          </tx:advice>
      
          <!-- 第二步:配置事务切入 -->
          <aop:config>
              <!-- 指定切入点为 mapper 包下所有接口中的所有方法 -->
              <aop:pointcut id="txPointCut" expression="execution(* com.sys.mapper.*.*(..))"/>
              <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
          </aop:config>
      
      </beans>
      
  • 其他代码不变

    • 测试结果:执行到 delete 方法时,依然报错,但是数据库没有新增数据,事务回滚成功。

在这里插入图片描述

在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值