Spring5框架七:事务

事务

一、概述
  1. 概念

    事务是数据库操作的最基本单元,逻辑上的一组操作,要么都成功,如果有一个失败所有操作都失败

  2. 四个特性 - ACID
    原子性(Atomicity)

    该组操作在功能上不可分割,要成功必须都成功,有一个执行不通过则整组操作全部失败

    一致性(Consistency)

    操作之前和操作之后的总量是不变的

    隔离性(Isolation)

    多事务操作,彼此之间不会产生影响。

    持久性(Duration)

    提交后会对数据库中的数据产生对应的改变,这种改变是持久的

二、搭建事务操作环境
  1. 业务逻辑
  2. 在数据库中创建数据表,添加数据
  3. 创建service,搭建dao

    ① service中注入dao,dao中注入JdbcTemplate,JdbcTemplate中注入DataSource

    ② 在dao中写入具体的事务方法

    ③ 在service中写入业务逻辑,并进行事务管理

  4. 事务管理

    ① 开启事务

    ② 进行业务操作

    ③ 如果没有异常,提交事务

    ④ 如果有异常,回滚事务

  5. 操作代码示例
    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"
           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">
        <!-- 组件扫描 -->
        <context:component-scan base-package="com.atguigu.spring5"/>
    
        <!-- 数据库连接池 -->
        <context:property-placeholder location="jdbc.properties"/>
        <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
            <property name="url" value="${prop.url}"/>
            <property name="username" value="${prop.username}"/>
            <property name="password" value="${prop.password}"/>
            <property name="driverClassName" value="${prop.driverClassName}"/>
        </bean>
    
        <!-- 创建JdbcTemplate对象 -->
        <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
            <!-- 注入DataSource -->
            <property name="dataSource" ref="dataSource"/>
        </bean>
    </beans>
    
    Service
    public class UserService {
        // 注入dao
        @Autowired
        private UserDAO userDAO;
    
        public void accountMoney() {
            try {
                // 第一步 开启事务
                // 第二步 业务操作
                // lucy少100
                userDAO.reduceMoney();
                // 模拟异常
                int i = 10/0;
                // mary多100
                userDAO.addMoney();
                // 第三步:没有异常,提交事务
            } catch (Exception e) {
                // 第四步:回滚事务
            }
        }
    }
    
    dao
    @Repository
    public class UserDAOImpl implements UserDAO{
        // 注入JdbcTemplate
        @Autowired
        private JdbcTemplate jdbcTemplate;
        
        // 少钱
        @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");
        }
    }
    
三、Spring中的事务操作
  1. 事务添加到JavaEE三层结构里面的Service层(业务逻辑层)
  2. 在Spring进行事务管理有两种方式:

    编程式:就是每次都要书写事务的各个步骤,会造成代码臃肿,且在后期不方便修改

    声明式:有两种形式,基于注解、基于xml配置文件

  3. 在Spring中进行声明式事务管理,底层使用AOP原理
  4. Spring事务管理API

    ① 提供了一个接口,代表事务管理器,这个接口针对不同的框架提供不同的实现类

    在这里插入图片描述

四、基于注解 - 声明式事务操作
  1. 在spring配置文件中配置事务管理器
     <!-- 创建事务管理器 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    
  2. 在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"
           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">
        
    <beans>
        <!-- 开启事务注解 -->
        <tx:annotation-driven transaction-manager="transactionManager" />
    </beans>
    
  3. 在service上(或service内部的方法上)添加事务注解:@Transactional

    ① 如果添加到了类上面,相当于里面的所有方法都添加了事务注解

    ② 如果添加到了具体的方法上,就只有该方法添加了事务注解

    @Service
    @Transactional
    public class UserService {
        // 注入dao
        @Autowired
        private UserDAO userDAO;
        public void accountMoney() {
                // 1.lucy少100
                userDAO.reduceMoney();
                // 2.模拟异常
                int i = 10/0;
                // 3.mary多100
                userDAO.addMoney();
        }
    }
    
  4. @Transactional注解中的参数

    propagation:事务传播行为

    ioslation:事务隔离级别

    timeout:超时时间

    readOnly:是否只读

    rollbackFor:回滚

    noRollbackFor:不回滚

  5. propagation:事务传播行为
    Service
    @Transactional(propagation = Propagation.REQUIRED)
    public class UserService {
    
  6. ioslation:事务隔离级别
    @Service
    @Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.REPEATABLE_READ)
    public class UserService {
    
  7. timeout:超时时间

    事务需要在一定的时间内进行提交,如果超过时间未提交,就会回滚

    默认值是-1,表示不超时,设置的值的单位为秒

    @Service
    @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.REPEATABLE_READ, timeout=-1)
    public class UserService {
    
  8. readOnly:是否只读

    读:查询操作;写:增删改操作

    readOnly的默认值为false,即增删改查都可进行;设置为true,只可以进行读操作,即只可查询

  9. rollbackFor:回滚

    设置出现哪些异常会进行事务回滚

  10. noRollbackFor:不回滚

    设置出现哪些异常不会进行事务回滚

五、事务知识补充
  1. 事务传播行为的分类

    注:描述中的当前的方法指代的是被调用的方法

    传播属性描述
    REQUIRED如果有事务在运行,当前的方法就在这个事务内运行,否则,就启动一个新的事务,并在自己的事务内运行(@Transation注解的默认参数
    REQUIRED_NEW当前的方法必须启动新事物,并在它自己的事务内运行,如果有事务正在运行,必须将它挂起
    SUPPORTS如果有事务在运行,当前的方法就在这个事务内运行,否则它可以不运行在事务中
    NOT_SUPPORTED当前的方法不应该运行在事务中,如果有运行的事务,就将它挂起
    MANDATORY当前的方法必须运行在事务内部,如果没有正在运行的事务,就抛出异常
    NEVER当前的方法不应该运行在事务中,如果有运行的事务,就抛出异常
    NESTED如果有事务在运行,当前的方法就应该在这个事务的嵌套事务内运行,否则,就启动一个新的事务,并在它自己的事务内运行。
    具体举例说明,详见博文:[带你读懂Spring 事务——事务的传播机制 (https://zhuanlan.zhihu.com/p/148504094#circle=on)
  2. 事务的隔离性会引发的三种读问题

    事务的特性中有隔离性,即多事务操作之间互不影响。如果不考虑隔离性,会产生脏读、不可重复读和幻读现象。

    脏读:一个未提交事务读取到了另一个未提交事务修改的数据。如果另一事务发生回滚,那么就会发生数据错误。

    不可重复读:一个未提交事务读取到了另一个已提交事务修改的数据。因为事务之间应该是互不影响的,正常情况,应该是该事务提交之后,才可以读到被其它事务修改的数据

    幻读:一个未提交的事务读取到了另一个已提交事务插入的数据或没有读到另一个已提交事务删除的数据

    具体说明,可参考:带你读懂Spring 事务——事务的隔离级别 - 知乎 (zhihu.com)
  3. 设置事务的隔离性
    隔离级别脏读不可重复读幻读
    READ UNCOMMITTED(读未提交)
    READ COMMITTED(读已提交)
    REPEATABLE READ(可重复读)(默认)
    SERIALIZABLE(串行化)
六、基于xml - 声明式事务操作
  1. 配置事务管理器
  2. 配置通知
  3. 配置切入点和切面
    <?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
                               http://www.springframework.org/schema/aop/spring-aop.xsd">
        <!-- 组件扫描 -->
        <context:component-scan base-package="com.atguigu.spring5"/>
    
        <!-- 数据库连接池 -->
        <context:property-placeholder location="jdbc.properties"/>
        <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
            <property name="url" value="${prop.url}"/>
            <property name="username" value="${prop.username}"/>
            <property name="password" value="${prop.password}"/>
            <property name="driverClassName" value="${prop.driverClassName}"/>
        </bean>
    
        <!-- 创建JdbcTemplate对象 -->
        <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
            <!-- 注入DataSource -->
            <property name="dataSource" ref="dataSource"/>
        </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="account*" propagation="REQUIRED" isolation="REPEATABLE_READ"/>
            </tx:attributes>
        </tx:advice>
    
        <!-- 3.配置切入点和切面 -->
        <aop:config>
            <!-- 配置切入点 -->
            <aop:pointcut id="pt" expression="execution(* com.atguigu.spring5.service.UserService.*(..))"/>
            <!-- 配置切面 第一个参数是通知 第二个参数是切入点 该切面的配置就是将通知(增强)匹配到对应的切入点上 -->
            <aop:advisor advice-ref="txadvice" pointcut-ref="pt" />
    
        </aop:config>
    
    </beans>
    
七、完全注解开发模式
  1. 创建配置类
    // 指定其是配置类
    @Configuration
    // 开启组件扫描
    @ComponentScan(basePackages = "com.atguigu.spring5")
    // 开启事务
    @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/bookdb");
            dataSource.setUsername("root");
            dataSource.setPassword("7989775nx");
            return dataSource;
        }
    
        // 创建JdbcTemplate对象
        // 在IOC容器中,已经存在了DataSource,在JdbcTemplate的方法中的参数可通过组件扫描达到自动匹配
        @Bean
        public JdbcTemplate getJdbcTemplate(DruidDataSource dataSource) {
            JdbcTemplate jdbcTemplate = new JdbcTemplate();
            // 注入DataSource
            jdbcTemplate.setDataSource(dataSource);
            return jdbcTemplate;
        }
    
        // 创建事务管理器
        @Bean
        public DataSourceTransactionManager getTransactionManager(DruidDataSource dataSource) {
            DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
            transactionManager.setDataSource(dataSource);
            return transactionManager;
        }
    }
    
    
  2. 测试类
    public class TestAccount {
        @Test
        public void test2() {
            AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TxConfig.class);
            UserService userService = context.getBean("userService", UserService.class);
            userService.accountMoney();
        }
    }
    
参考资料
  1. 事务的四大特性_今天你学习了么的博客-CSDN博客_事务的四个特性
  2. 带你读懂Spring 事务——事务的传播机制 - 知乎 (zhihu.com)
  3. 带你读懂Spring 事务——事务的隔离级别 - 知乎 (zhihu.com)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

e_nanxu

感恩每一份鼓励-相逢何必曾相识

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值