Spring5 框架【五】事务操作

视频链接:2020最新版SpringCloud框架开发教程-周阳
文章源码:https://github.com/geyiwei-suzhou/spring5/

事务概念

  1. 什么是事务?
    (1)事务是数据库操作最基本单元,逻辑上一组操作,要么都成功,如果有一个失败所有操作都失败
    (2)典型场景:银行转账
    • lucy转账100元给mary
    • lucy少100,mary多100
  2. 事务四个特性(ACID)
    (1)原子性
    (2)一致性
    (3)隔离性
    (4)持久性

搭建事务操作环境

Spring5 框架【五】事务操作

  1. 创建数据库表,添加记录

    CREATE TABLE `t_account` (
      `id` varchar(20) NOT NULL,
      `username` varchar(50) DEFAULT NULL,
      `money` int(11) DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    
    INSERT INTO `t_account` VALUES ('1', 'lucy', 1000);
    INSERT INTO `t_account` VALUES ('2', 'mary', 1000);
    
  2. 创建service,搭建dao,完成对象创建和注入关系
    (1)service注入dao,在dao注入JdbcTemplate,在JdbcTemplate注入DataSource

    package com.antherd.spring5.dao;
    
    public interface UserDao {
    
      // 少钱
      public void reduceMoney();
    
      // 多钱
      public void addMoney();
    }
    
    package com.antherd.spring5.dao;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.jdbc.core.JdbcTemplate;
    import org.springframework.stereotype.Repository;
    
    @Repository
    public class UserDaoImpl implements UserDao {
    
      @Autowired
      private JdbcTemplate jdbcTemplate;
    
      // lucy转账100给mary
      // 少钱
      @Override
      public void reduceMoney() {
        String sql = "update t_account set money = money - ? where username = ?";
        jdbcTemplate.update(sql, 100, "lucy");
      }
    
      // 多钱
      @Override
      public void addMoney() {
        String sql = "update t_account set money = money + ? where username = ?";
        jdbcTemplate.update(sql, 100, "mary");
      }
    }
    
    package com.antherd.spring5.service;
    
    import com.antherd.spring5.dao.UserDao;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    @Service
    public class UserService {
    
      // 注入dao
      @Autowired
      private UserDao userDao;
    
      // 转账的方法
      public void accountMoney() {
        // lucy少100
        userDao.reduceMoney();
        // mary多100
        userDao.addMoney();
      }
    }
    

    测试

    @Test
    public void testAccount() {
      ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
      UserService userService = context.getBean("userService", UserService.class);
      userService.accountMoney();
    }
    

事务场景引入

上面代码,如果正常执行没有问题,但是如果代码执行过程中出现异常,有问题
模拟异常,在UserService中reduceMoney和addMoney之间添加异常代码

// 模拟异常
int i = 10 / 0;

运行发现,lucy的钱减少了,mary的钱并没有增加
引入事务

try {
   // 第一步 开启事务

   // 第二步 进行业务操作
   // lucy少100
   userDao.reduceMoney();

   // 模拟异常
   int i = 10 / 0;

   // mary多100
   userDao.addMoney();

   // 第三步 没有发生异常,提交事务
 } catch (Exception e) {
   // 第四步 出现异常,事务回滚
 }

事务管理介绍

  1. 事务添加到JavaEE三层结构里面Service层(业务逻辑层)
  2. 在Spring进行事务管理操作
    (1)有两种方式:编程式事务管理和声明式事务管理(常用)
  3. 声明式事务管理
    (1)基于注解方式(常用)
    (2)基于xml配置文件方式
  4. 在Spring进行声明式事务管理,底层使用AOP原理
  5. Spring事务管理API
    (1)提供了一个接口PlatformTransactionManager,代表事务管理器,这个接口针对不同的框架提供不同的实现类

注解声明式事务管理

  1. 在spring配置文件配置事务管理器

    <!-- 创建事务管理器 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
      <!-- 注入数据源 -->
      <property name="dataSource" ref="dataSource" />
    </bean>
    
  2. 在spring配置文件,开启事务注解
    (1)在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
    

    (2)开启事务注解

    <!-- 开启事务注解 -->
    <tx:annotation-driven transaction-manager="transactionManager" />
    
  3. 在service类上面(或者service类里面方法上面)添加事务注解
    (1)@Transactional,这个注解添加到类上面,也可以添加到方法上面
    (2)如果把这个注解添加类上面,这个类里面所有的方法都添加事务
    (3)如果把这个注解添加方法上面,为这个方法添加事务

    @Service
    @Transactional
    public class UserService
    

Spring声明式事务管理 - 事务参数(传播行为)

  1. 在service类上面添加注解@Transactional,在这个注解里面可以配置事务相关参数
    Spring5 框架【五】事务操作
  2. propagation:事务传播行为
    (1)多事务方法之间进行调用,这个过程中事务是如何进行管理的
    事务的传播行为可以由传播属性指定。Spring定义了7中传播行为
传播属性 描述
REQUIRED 如果有事务在运行,当前的方法就在这个事务内运行,否则,就启动一个新的事务,并在自己的事务内运行
REQUIRED_NEW 当前的方法必须启动新事物,并在它自己的事务内运行,如果有事务正在运行,应该将它挂起
SUPPORTS 如果有事务在运行,当前的方法就在这个事务内运行,否则它可以不运行在事务中
NOT_SUPPORTED 当前的方法不应该运行在事务中,如果有运行的事务,将它挂起
MANDATORY 当前的方法必须运行在事务内部,如果没有正在运行的事务,就抛出异常
NEVER 当前的方法不应该运行在事务中,如果有运行的事务,就抛出异常
NESTED 如果有事务在运行,当前的方法就应该在这个事务的嵌套事务内运行,否则,就启动一个新的事务,并在它自己的事务内运行

常用的两种事务传播行为Spring5 框架【五】事务操作
3. ioslation:事务隔离级别
(1)事务有特性称为隔离性,多事务操作之间不会产生影响。不考虑隔离性产生很多问题
(2)有三个读问题:脏读、不可重复读、虚(幻)读
(3)脏读:一个未提交事务读取到另一个未提交事务的数据
Spring5 框架【五】事务操作
(4)不可重复读:一个未提交事务读取到另一个提交事务修改数据
Spring5 框架【五】事务操作
(5)虚读:一个未提交事务读取到另一个提交事务添加数据
(6)解决:通过设置事务隔离级别,解决读问题

- 脏读 不可重复读 幻读
READ UNCOMMITTED(读未提交)
READ COMMITTED(读已提交)
REPEATABLE(可重复读)
SERIALIZABLE(串行化)

MYSQL 默认隔离级别为REPEATABLE_READ
SQLSERVER 默认隔离级别为READ_COMMITTED
Oracle 默认隔离级别为 READ_COMMITTED
4. timeout:超时时间
(1)事务需要在一定时间内进行提交,如果不提交进行回滚
(2)默认值是-1,设置时间以秒单位进行计算
5. readOnly:是否只读
(1)读:查询,写:添加修改删除操作
(2)readOnly默认值false,表示可以查询,可以添加修改删除操作
(3)设置readOnly值是true,设置成true之后,只能查询
6. rollbackFor:回滚
(1)设置出现哪些异常进行事务回滚
7. noRollbackFor:不回滚
(1)设置出现哪些异常不进行事务回滚

XML声明式事务管理

  1. 在spring配置文件中进行配置
    第一步 配置事务管理器
    第二步 配置通知
    第三步 配置切入点和切面
    新建bean2.xml
    <?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:aop="http://www.springframework.org/schema/aop"
      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/context http://www.springframework.org/schema/context/spring-context.xsd
      http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
      http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
    ">
      <!-- 组件扫描 -->
      <context:component-scan base-package="com.antherd"></context:component-scan>
    
      <!-- 数据库连接池 -->
      <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver" />
        <property name="url" value="jdbc:mysql://localhost:3306/user_db" />
        <property name="username" value="root" />
        <property name="password" value="123456" />
      </bean>
    
      <!-- JdbcTemplate对象 -->
      <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <!-- 注入dataSource -->
        <property name="dataSource" ref="dataSource"></property>
      </bean>
    
      <!-- 1. 创建事务管理器 -->
      <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!-- 注入数据源 -->
        <property name="dataSource" ref="dataSource" />
      </bean>
    
      <!-- 2. 配置通知 -->
      <tx:advice id="txadvice">
        <!-- 配置事务参数 -->
        <tx:attributes>
          <!-- 指定哪种规则的方法上面添加事务 -->
          <tx:method name="accountMoney" propagation="REQUIRED"/>
    <!--      <tx:method name="account*"/>-->
        </tx:attributes>
      </tx:advice>
    
      <!-- 3. 配置切入点和切面 -->
      <aop:config>
        <!-- 配置切入点 -->
        <aop:pointcut id="pt" expression="execution(* com.antherd.spring5.service.UserService.*(..))" />
        <!-- 配置切面 -->
        <aop:advisor advice-ref="txadvice" pointcut-ref="pt" />
      </aop:config>
    
    </beans>
    
    取消UserService类上的@Transactional注解
    添加测试方法
    @Test
    public void testAccount1() {
      ApplicationContext context = new ClassPathXmlApplicationContext("bean2.xml");
      UserService userService = context.getBean("userService", UserService.class);
      userService.accountMoney();
    }
    

完全注解声明式事务管理

  1. 创建配置类,使用配置类替代xml配置文件

    package com.antherd.spring5.config;
    
    import com.alibaba.druid.pool.DruidDataSource;
    import javax.sql.DataSource;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.jdbc.core.JdbcTemplate;
    import org.springframework.jdbc.datasource.DataSourceTransactionManager;
    import org.springframework.transaction.annotation.EnableTransactionManagement;
    
    @Configuration // 配置类
    @ComponentScan(basePackages = "com.antherd") // 组件扫描
    @EnableTransactionManagement // 开启事务
    public class TxConfig {
    
      // 创建数据库连接池
      @Bean
      public DruidDataSource getDruidDataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/user_db");
        dataSource.setUsername("root");
        dataSource.setPassword("123456");
        return dataSource;
      }
    
      // 创建JdbcTemplate对象
      @Bean
      public JdbcTemplate getJdbcTemplate(DataSource dataSource) {
        // 到ioc容器中根据类型找到dataSource
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        // 注入dataSource
        jdbcTemplate.setDataSource(dataSource);
        return jdbcTemplate;
      }
    
      // 创建事务管理器
      @Bean
      public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource) {
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
        transactionManager.setDataSource(dataSource);
        return transactionManager;
      }
    }
    
  2. UserService类上添加@Transactional注解
    测试方法

    @Test
    public void testAccount2() {
      ApplicationContext context = new AnnotationConfigApplicationContext(TxConfig.class);
      UserService userService = context.getBean("userService", UserService.class);
      userService.accountMoney();
    }
    
上一篇:Centos 8 搭建时钟服务器


下一篇:centos-ntp服务器设置