第5章 Spring的事务管理

5.1 Spring事务管理概述

5.11 事务管理的核心接口

在Spring的所有jar包中,有一个名spring-tx-4.3.6RELEAS的jar包,是提供事务管理的依赖包。在该包的org.springframework.transaction包中,三个核心接口文件分别是PlatformTransactionManagerTransactionDefinitionTransactionStatus

1.PlatfromTransactionManager

PlatfromTransactionManager接口是Spring提供的平台事务管理器,主要用于管理事务。有3个事务操作的方法:

TransactionStatus getTransaction( TransactionDefinition definition ):用于获取事务状态信息

void commit( TransactionStatus status ):提交事务

void rollback( Transaction status ):回滚事务

PlatformTransactionManager接口只是代表事务管理的接口,并不知道底层是如何管理事务的,具体如何管理事务则由它的实现类来完成。该接口常见的几个实现类如下:

(1)、用于配置JDBC数据源的事务管理器:
org.springframework.jdbc.datasource.DataSourceTransactionManager
(2)、用于配置Hibernate的事务管理器:
org.springframework.orm.hibernate4.HibernateTransactionManager
(3)、用于配置全局事务管理器:
org.springframework.transaction.jta.JtaTransactionManager

当底层采用不同的持久层技术时,系统只需使用不同的实现类。

2.TransactionDefinition

事务定义(描述)的对象,该对象中定义了事务规则,并提供获取事务相关信息的方法,具体如下:

String getName():获取事务对象名称

int getlsolationLevel:获取事务的隔离级别

int getPropagationBehavior():获取事务的传播行为

int getTimeout():获取事务的超时时间

boolean isReadOnly():获取事务的是否只读

传播行为:指在同一个方法中,不同操作前后所使用的事务。传播行为有很多种:

传播行为可以控制是否需要创建事务以及如何创建事务,查询不影响数据的改变,不需要事务管理;插入、更新和删除需要事务管理。如果没有指定事务的传播行为,Spring默认传播行为是REQUIRED(required)

3.TransactionStatus

TransactionStatus接口是事务的状态,它描述了某一时间点上事务的状态信息,该接口有6个方法:

void flush():刷新事务

boolean hasSavepoint():获取是否存在保存点

boolean isCompleted():获取事务是否完成

boolean isNewTransaction():获取是否是新事物

boolean isRollbackOnly():获取是否回滚

void setRollbackOnly():设置事务回滚

5.12 事务管理的方式:

传统的编程式事务管理:通过编写代码实现的事务管理,包括定义事务的开始、正常执行后的事务提交和异常时的事务回滚

声明式事务管理:通过AOP技术实现的事务管理,主要思想是将事务管理作为一个“切面”代码单独编写,然后通过AOP技术将管理“切面”代码织入到业务目标类中。

5.2 声明式事务管理

5.21 基于XML方式的声明式事务

tx命名空间下提供了<tx:advice>元素来配置事务的通知(增强处理)。当使用<tx:advice>元素配置了事务的增强处理后,就可以通过编写AOP配置,让Spring自动对目标生成代理。

配置<tx:advice>元素时,通常需要指定id和transaction-manager属性。transaction-manager属性用于指定事务管理器。除此之外,还需要配置一个<tx:attributes>子元素,该子元素可以通过配置多个<tx:method>子元素来配置执行事务的细节。

 转账方法编写:

        /**
     *  转账
     *  inUser:收款人
     *  outUser:汇款人
     *  money:收款金额
    */
    public void transfer(String outUser, String inUser, Double money) {
        // 收款时,收款用户的余额=现有余额+所汇金额
        this.jdbcTemplate.update("update account set balance = balance +? "
                + "where username = ?",money, inUser);
        // 模拟系统运行时的突发性问题
        int i = 1/0;
        // 汇款时,汇款用户的余额=现有余额-所汇金额
        this.jdbcTemplate.update("update account set balance = balance-? "
                + "where username = ?",money, outUser);
    }                

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:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx" 
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
    http://www.springframework.org/schema/tx 
    http://www.springframework.org/schema/tx/spring-tx-4.3.xsd
    http://www.springframework.org/schema/context 
    http://www.springframework.org/schema/context/spring-context-4.3.xsd
    http://www.springframework.org/schema/aop 
    http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
    
    <!-- 1.配置数据源 -->
    <bean id="dataSource222" 
    class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    
        <!--数据库驱动 -->
        <property name="driverClassName" value="com.mysql.jdbc.Driver" />
        
        <!--连接数据库的url -->
        <property name="url" value="jdbc:mysql://localhost/spring" />
        
        <!--连接数据库的用户名 -->
        <property name="username" value="root" />
        
        <!--连接数据库的密码 -->
        <property name="password" value="****" />
   </bean>
   
   <!-- 2.配置JDBC模板 -->
   <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
          
         <!-- 默认必须使用数据源 -->
         <property name="dataSource" ref="dataSource222" />
         
   </bean>
   
   <!--3.定义id为accountDao的Bean -->
   <bean id="accountDao" class="com.itheima.jdbc.AccountDaoImpl">
   
         <!-- 将jdbcTemplate注入到AccountDao实例中 -->
         <property name="jdbcTemplate" ref="jdbcTemplate" />
   </bean>    
   
   <!-- 4.事务管理器,依赖于数据源 -->
   <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        
        <property name="dataSource" ref="dataSource222" />
   </bean>    
   
   <!-- 5.编写通知:对事务进行增强(通知),需要编写对切入点和具体执行事务细节 -->
   <tx:advice id="txAdvice123" transaction-manager="transactionManager">
        <tx:attributes>
            <!-- name:*表示任意方法名称,传播行为,隔离级别,是否只读-->
            <tx:method name="*" propagation="REQUIRED" isolation="DEFAULT" read-only="false" />
        </tx:attributes>
    </tx:advice>
    
    <!-- 6.编写AOP,让spring自动对目标生成代理,需要使用AspectJ的表达式 -->
    <aop:config>
        <!-- 切入点 -->
        <aop:pointcut expression="execution(* com.itheima.jdbc.*.*(..))"
            id="txPointCut111" />
        <!-- 切面:将切入点与通知整合 -->
        <aop:advisor advice-ref="txAdvice123" pointcut-ref="txPointCut111" />
    </aop:config>
</beans>

测试类:获取实例,调用方法,常规操作

@Test///基于XML方式的声明式事务
    public void xmlTest(){
        ApplicationContext applicationContext = 
                new ClassPathXmlApplicationContext("applicationContext.xml");
        
        // 获取AccountDao实例
        AccountDao accountDao = 
                (AccountDao)applicationContext.getBean("accountDao");
        
        // 调用实例中的转账方法
        accountDao.transfer("Jack", "Rose", 100.0);
        
        // 输出提示信息
        System.out.println("转账成功!");
    }

5.22 基于Annotation(注解)方式的声明式事务

两个套路

1.在Spring容器中注册事务注解驱动:

<tx:annotation-driven transaction-manager="transactionManager"/>

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:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx" 
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
    http://www.springframework.org/schema/tx 
    http://www.springframework.org/schema/tx/spring-tx-4.3.xsd
    http://www.springframework.org/schema/context 
    http://www.springframework.org/schema/context/spring-context-4.3.xsd
    http://www.springframework.org/schema/aop 
    http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
    <!-- 1.配置数据源 -->
    <bean id="dataSource" 
    class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    
        <!--数据库驱动 -->
        <property name="driverClassName" value="com.mysql.jdbc.Driver" />
        
        <!--连接数据库的url -->
        <property name="url" value="jdbc:mysql://localhost/spring" />
        
        <!--连接数据库的用户名 -->
        <property name="username" value="root" />
        
        <!--连接数据库的密码 -->
        <property name="password" value="17251104238" />
    </bean>
    
    <!-- 2.配置JDBC模板 -->
    <bean id="jdbcTemplate" 
            class="org.springframework.jdbc.core.JdbcTemplate">
        
        <!-- 默认必须使用数据源 -->
        <property name="dataSource" ref="dataSource" />
    
    </bean>
    
    <!--3.定义id为accountDao的Bean -->
    <bean id="accountDao" class="com.itheima.jdbc.AccountDaoImpl">
        <!-- 将jdbcTemplate注入到AccountDao实例中 -->
        <property name="jdbcTemplate" ref="jdbcTemplate" />
    </bean>
    
    <!-- 4.事务管理器,依赖于数据源 -->
    <bean id="transactionManager" class=
     "org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>    
    
    <!-- 5.注册事务管理器驱动 -->
    <tx:annotation-driven transaction-manager="transactionManager"/>
    
</beans>

2.在方法transfer()前加注解:

@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, readOnly = false)

方法:

@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, readOnly = false)
    public void transfer(String outUser, String inUser, Double money) 
    {
        // 收款时,收款用户的余额=现有余额+所汇金额
        this.jdbcTemplate.update("update account set balance = balance +? "
                + "where username = ?",money, inUser);
        
        // 模拟系统运行时的突发性问题
        //int i = 1/0;
        // 汇款时,汇款用户的余额=现有余额-所汇金额
        this.jdbcTemplate.update("update account set balance = balance -? "
                + "where username = ?",money, outUser);
    }
原文地址:https://www.cnblogs.com/shoulinniao/p/10917424.html