• Spring 框架的事务管理


    1. Spring 框架的事务管理相关的类和API

    • PlateformTransactionManager 接口: 平台事务管理器(真正管理事务的类);
    • TransactionDefinition 接口: 事务定义信息(事务的隔离级别,传播行为,超时,只读等);
    • TransactionStatus 接口: 事务的状态;
    • 平台事务管理器真正管理事务对象,根据事务定义信息(TransactionDefinition)进行事务管理,在管理事务
      中产生的一些状态记录到TransactionStatus中;

    1.1 PlateformTransactionManager 接口中实现类和常用的方法

    1. 接口的实现类
      • 如果使用 Spring 框架的JDBC模板或者MyBatis框架,需要选择DataSourceTransactionManager实现类;
      • 如果使用的是 Hibernate 框架,需要选择HibernateTransactionManager实现类;
    2. 该接口的常用方法
      • void commit(TransactionStatus status)
      • TransactionStatus getTransaction(TransactionDefinition definition)
      • void rollback(TransactionStatus status)

    1.2 TransactionDefinition

    1. 事务隔离级别的常量

      • static int ISOLATION_DEFAULT: 采用数据库的默认隔离级别;
      • static int ISOLATION_READ_UNCOMMITTED
      • static int ISOLATION_READ_COMMITTED
      • static int ISOLATION_REPEATABLE_READ
      • static int ISOLATION_SERIALIZABLE
    2. 事务的传播行为常量(一般使用默认值)

      • 事务的传播行为:解决的是业务层之间的方法调用!
      • 保证 A,B 在同一个事务中:
        • PROPAGATION_REQUIRED(默认值): A中有事务,使用A中的事务;如果没有,B会开启一个新的事务,
          将A包含进来(保证A,B在同一个事务中);
        • PROPAGATION_SUPPORTS: A中有事务,使用A中的事务;如果A中没有事务,那么B也不使用事务;
        • PROPAGATION_MANDATORY: A中有事务,使用A中的事务; 如果A中没有事务,抛出异常;
      • 保证 A,B 没有在同一个事务中:
        • PROPAGATION_REQUIRES_NEW: A中有事务,将A中的事务挂起,B创建一个新的事务,
          (保证A,B不在同一个事务中);
        • PROPAGATION_NOT_SUPPORTED: A中有事务,将A中的事务挂起;
        • PROPAGATION_NEVER: A中有事务,抛出异常;
      • PROPAGATION_NESTED: 嵌套事务,当 A 执行之后,就会在这个位置设置一个保存点,如果B没有问题,
        执行通过;如果B出现异常,根据需求回滚(可以回滚到保存点或最初始的状态);

    2. 搭建事务管理转账案例的环境

    2.1 导入 jar 包

    • Spring 框架的基本开发包(6个);
    • Spring 的传统AOP的开发包
      • spring-aop-4.3.10.RELEASE
      • org.aopalliance-1.10.0 (在 Spring 依赖包中)
    • aspectJ 的开发包
      • org.aspectj.weave-1.6.8.RELEASE.jar (在 Spring 依赖包中)
      • spring-aspects-4.3.10.RELEASE.jar
    • JDBC 模板所需 jar 包
      • mysql-connector-java.jar: MySql 驱动包;
      • Spring-jdbc.jar;
      • Spring-tx.jar: Spring 事务包;
    • c3p0 的 jar 包: com.mchange.v2.c3p0-0.9.1.2.jar

    2.2 创建对应的包结构和类

    • com.itheima.demo
      • AccountService
      • AccountServiceImpl
      • AccountDao
      • AccountDaoImpl
    // AccountDao.java
        public class AccountDao {
            // 扣钱
            public void outMoney(String out, double money);
    
            // 加钱
            public void inMoney(String in, double money);
        }
    
    // AccountService.java
        public class AccountService{
            public void pay(String out,String in, double money);
        }
    
    // AccountDaoImpl.java
        // 第一种方式, 使用 jdbc 模板类
        public class AccountDaoImpl implements AccountDao{
            // 使用 jdbc 模板操作数据库
            // 在配置文件中注入 jdbcTemplate 对象
            private JdbcTemplate jdbcTemplate;
            public void setJdbcTemplate(JdbcTemplate jdbcTemplate){
                this.jdbcTemplate = jdbcTemplate;
            }
    
            // 扣钱
            public void outMoney(String out, double money){
                jdbcTemplate.update(sql,args);
            }
    
            // 加钱
            public void inMoney(String out, double money){
                jdbcTemplate.update(sql,args);
            }
        }
    
        // 第二种方式, 继承 JdbcDaoSupport类, 该类中包含 jdbcTemplate,并且提供了 set方法
        //           applicationContext.xml 中仍需要注入 jdbcTemplate 对象
    
        public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao{
    
            // 扣钱
            public void outMoney(String out, double money){
                this.getJdbcTemplate().update(sql,args);
            }
    
            // 加钱
            public void inMoney(String in, double money){
                this.getJdbcTemplate().update(sql,args);
            }
        }
    
        // 第三种方式: applicationContext.xml 中无需注入 jdtcTemplate 对象
        //           只需要注入连接池对象,
        // JdbcDaoSupport 类内部,在 jdbcTemplate 对象为 null 时, 根据连接池创建 jdbcTemplate 对象
    
        public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao{
    
            // 扣钱
            public void outMoney(String out, double money){
                this.getJdbcTemplate().update("UPDATE t_account SET"
                                    +"money = money - ? WHERE name = ?",money,out);
            }
    
            // 加钱
            public void inMoney(String in, double money){
                this.getJdbcTemplate().update("UPDATE t_account SET"
                                    +"money = money - ? WHERE name = ?",money,in);
            }
        }
    
    
    // AccountServiceImpl.java
        public class AccountServiceImpl implements AccountService{
    
            private AccountDao accountDao;
            public void setAccountDao(AccountDao accountDao){
                this.accountDao = accountDao;
            }
    
            // 实现转账功能
            public void pay(String out, String in, double money){
                accountDao.outMoney(out,money);
                accountDao.inMoney(in,money);
            }
        }
    
    
    // 测试类
        @RunWith(SpringJUnit4ClassRunner.class)
        @ContextConfiguration("classpath:applicationContext.xml")
        public class Demo{
    
            @Resource(name="accountService")
            private AccountServiceImpl accountService;
    
            @Test
            public void fun(){
                // 测试支付方法
                accountService.pay("张三","李四",100);
            }
        }
    

    2.3 applicationContext.xml

    <!-- 管理c3p0 连接池 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.jdbc.Driver"/>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/mydb1"/>
        <property name="user" value="root"/>
        <property name="password" value="root"/>
    </bean>
    
    <!-- 管理JDBC的模板类 -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" value="dataSource"/>
    </bean>
    
    <!-- 配置业务层和持久层 -->
    <bean id="accountService" class="com.itheima.demo.AccountServiceImpl">
        <property name="accountDao" value="accountDao"/>
    </bean>
    
    <!-- 第一种和第二种方式, 需要注入 jdbcTemplate 对象 -->
    <bean id="accountDao" class="com.itheima.demo.AccountDaoImpl">
        <property name="jdbcTemplate" value="jdbcTemplate"/>
    </bean>
    
    <!-- 第三种方式, 只需要注入连接池 -->
    <bean id="accountDao" class="com.itheima.demo.AccountDaoImpl">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    

    2.4 Spring 框架事务管理的分类

    1. Spring 的编程式事务管理(不推荐使用)
      • 通过手动编写代码的方式完成事务的管理;
    2. Spring 的声明式事务管理(底层采用AOP的技术)
      • 通过一段配置的方式完成事务的管理;
    2.4.1 编程式的事务管理
    1. 手动编程的方式来管理事务,需要使用 TransactonTemplate模板类;
    // 具体步骤
    // 1. 配置一个事务管理器
    //    Spring 框架使用 PlatFormTransactionManager 接口来管理事务,需要使用到它的实现类
    <bean id="transactionManager"
                class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        // 底层操作事务的是 connection, 该对象存放在连接池
        // 事务管理器管理事务,需要从连接池中获取 connection 对象
        <property name="dataSource" ref="dataSource"/>
    </bean>
    
    // 2. 配置事务管理的模板
    <bean id="transactionTemplate"
                    class="org.springframework.transaction.support.TransactionTemplate">
        <property name="transactionManager" ref="transactionManager"/>
     </bean>
    
    // 3. 在需要进行事务管理的类中,注入事务管理的模板
    //     备注: 事务需要在业务层开启
        <bean id="accountService" class="com.itheima.demo.AccountServiceImpl">
            <property name="accountDao" ref="accountDao"/>
            <property name="transactionTemplate" ref="transactionTemplate"/>
        </bean>
    
    // 4. 在业务层使用模板管理事务
    public void AccountServiceImpl implements AccountService{
    
        private AccountDao accountDao;
        public void setAccountDao(AccountDao accountDao){
            this.accountDao = accountDao;
        }
    
        // 注入事务模板对象
        private TransactionTemplate transactionTemplate;
        public void setTransactionTemplate(TransactionTemplate transactionTemplate){
            this.transactionTemplate = transactionTemplate;
        }
    
        // 实现转账功能
        public void pay(final String out, final String in, final double money){
            transactionTemplate.execute(new TransactionCallbackWithoutResult(){
                protected void doInTransactionWithoutResult(TransactionStatus status){
                    // 扣钱
                    accountDao.outMoney(out,money);
    
                    // 异常
                    int a = 10 / 0;
    
                    // 加钱
                    accountDao.inMoney(in,money);
                }
            });
        }
    }
    
    2.4.2 声明式事务管理
    1. 声明式事务管理:即通过配置文件来完成事务管理(AOP 思想)
      • 基于AspectJ的XML方式;(重点掌握)
      • 基于AspectJ的注解方式;(重点掌握)
    基于AspectJ的XML方式
    // applicationContext.xml
    // 1. 配置一个事务管理器
    //    Spring 框架使用 PlatFormTransactionManager 接口来管理事务,需要使用到它的实现类
    <bean id="transactionManager"
                class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        // 底层操作事务的是 connection, 该对象存放在连接池
        // 事务管理器管理事务,需要从连接池中获取 connection 对象
        <property name="dataSource" ref="dataSource"/>
    </bean>
    
    // 2. 配置事务增强(通知)
    <tx:advice id="myAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <!--
              可以配置多个 method;
              name       : 绑定事务的方法名,可以使用通配符;
              propagation: 传播行为;
              isolation  : 隔离级别;
              read-only  : 缓存是否只读;
              timeout    : 超时信息;
              rollback-for: 发生哪些异常信息回滚;
              no-rollback-for: 发生哪些异常,不回滚;
              -->
    
              <tx:method name="pay" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>
    
    // 3. 配置 AOP 切面
    //    备注: 如果是自定义的切面,使用<aop:aspect>标签, 如果是系统自带的,使用<aop:advisor>标签
    <aop:config>
        <aop:advisor advice-ref="myAdvice"
                    pointcut="execution(public * com.itheima.demo.AccountServiceImpl.pay(..))"/>
    </aop:config>
    
    // 4. 持久层配置
        <bean id="accountService" class="com.itheima.demo.AccountServiceImpl">
            <property name="accountDao" ref="accountDao"/>
        </bean>
    
    基于AspectJ的注解方式(重点掌握,这是最简单的方式)
    // 1. 配置事务管理器
    <bean id="transactionManager"
                class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    
    // 2. 开启注解事务
    <tx:annotation-driven transaction-manager="transactionManager"/>
    
    // 3. 在业务层上添加一个注解: @Transactional
    // AccountServiceImpl.java
    
        // 如果在这个类上添加注解, 类中的所有方法全部都有事务
        @Transactional
        public class AccountServiceImpl implements AccountService{
    
            private AccountDao accountDao;
            public void setAccountDao(AccountDao accountDao){
                this.accountDao = accountDao;
            }
    
            // 实现转账功能
            // 如果在方法上添加,只有该方法有事务
            @Transactional
            public void pay(String out, String in, double money){
                accountDao.outMoney(out,money);
                accountDao.inMoney(in,money);
            }
        }
    

    参考资料

  • 相关阅读:
    文件系列--截取路径字符串,获取文件名
    ASP.NET State Service (ASP.NET 状态服务)已启动,并且客户端端口与服务器端口相同
    大小写字母,特殊字符,数字,四选一组合或者全组合,长度至少八位,验证
    设计模式-23种设计模式介绍
    &和&&区别
    GridView中Button多参数传参
    HTTP 错误 500.19
    Windows系统添加端口号
    win10安装IIS服务
    2019最新整理PHP面试题附答案
  • 原文地址:https://www.cnblogs.com/linkworld/p/7724625.html
Copyright © 2020-2023  润新知