• Spring事务管理


    事务概述

    什么事务

    逻辑上的一组操作,组成这组操作的各个单元,要么全都成功,要么全都失败。

    事务的特性(ACID)

    • 原子性:事务不可分割
    • 一致性:事务执行前后数据完整性保持一致
    • 隔离性:一个事务的执行不应该受到其他事务的干扰
    • 持久性:一旦事务结束,数据就持久化到数据库

    如果不考虑隔离性引发安全性问题

    读问题

    • 脏读 :一个事务读到另一个事务未提交的数据
    • 不可重复读 :一个事务读到另一个事务已经提交的 update 的数据,导致一个事务中多次查询结果不一致
    • 虚读、幻读 :一个事务读到另一个事务已经提交的 insert 的数据,导致一个事务中多次查询结果不一致。

    写问题

    • 丢失更新

    解决读问题

    设置事务的隔离级别

    • Read uncommitted :未提交读,任何读问题解决不了。
    • Read committed :已提交读,解决脏读,但是不可重复读和虚读有可能发生。
    • Repeatable read :重复读,解决脏读和不可重复读,但是虚读有可能发生。
    • Serializable :解决所有读问题。

    思维导图总结

    Spring的事务管理的API

    PlatformTransactionManager

    • PlatformTransactionManage
      平台事务管理器 是一个接口,下面有两个实现类
    • DataSourceTransactionManager
      底层使用JDBC管理事务
    • HibernateTransactionManager
      底层使用Hibernate管理事务

    TransactionDefinition

    事务定义信息:用于定义事务的相关的信息,隔离级别、超时信息、传播行为、是否只读

    TransactionStatus

    事务状态:用于记录在事务管理过程中,事务的状态的对象。

    事务管理的API的关系

    • Spring进行事务管理的时候,首先平台事务管理器根据事务定义信息进行事务的管理,
    • 在事务管理过程中,产生各种状态,将这些状态的信息记录到事务状态的对象中。

    思维导图总结

    Spring的事务的传播行为

    什么是传播行为

    一个业务方法当中,调用另一个业务的方法

    Spring中提供了七种事务的传播行为

    保证多个操作在同一个事务中

    • PROPAGATION_REQUIRED
      默认值,如果A中有事务,使用A中的事务,如果A没有,创建一个新的事务,将操作包含进来
    • PROPAGATION_SUPPORTS
      支持事务,如果A中有事务,使用A中的事务。如果A没有事务,不使用事务。
    • PROPAGATION_MANDATORY
      如果A中有事务,使用A中的事务。如果A没有事务,抛出异常。

    保证多个操作不在同一个事务中

    • PROPAGATION_REQUIRES_NEW
      如果A中有事务,将A的事务挂起(暂停),创建新事务,只包含自身操作。如果A中没有事务,创建一个新事务,包含自身操作。
    • PROPAGATION_NOT_SUPPORTED
      如果A中有事务,将A的事务挂起。不使用事务管理。
    • PROPAGATION_NEVER
      如果A中有事务,报异常。

    嵌套式事务

    • PROPAGATION_NESTED
      • 嵌套事务,如果A中有事务,按照A的事务执行,执行完成后,设置一个保存点
      • 执行B中的操作,如果没有异常,执行通过,如果有异常,可以选择回滚到最初始位置,也可以回滚到保存点

    思维导图总结

    Spring事务管理

    一、搭建Spring事务管理环境

    1.创建AoccuntDao

    public interface AccountDao {
        public void addMoney(String name, Double money);
        public void minusMoney(String name, Double money);
    }
    

    2.实现Dao接口

    public class AccountDaoImpl implements AccountDao {
    
        @Override
        public void addMoney(String name, Double money) {
    
        }
        @Override
        public void minusMoney(String name, Double money) {
    
        }
    }
    

    3.把Dao交给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: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/aop
    		http://www.springframework.org/schema/aop/spring-aop.xsd">
    
        <!--加载属性文件-->
        <context:property-placeholder location="classpath:jdbc.properties"/>
    
        <!--druid-->
        <bean id="druid" class="com.alibaba.druid.pool.DruidDataSource">
            <!--key值不能和name一样,所以一般加上jdbc前缀-->
            <property name="driverClassName" value="${jdbc.driverClass}"/>
            <property name="url" value="${jdbc.url}"/>
            <property name="username" value="${jdbc.username}"/>
            <property name="password" value="${jdbc.password}"/>
        </bean>
    
        <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
            <property name="dataSource" ref="druid"/>
        </bean>
    
        <bean id="accountDao" class="com.xzh.spring.demo.AccountDaoImpl"></bean>
    
    </beans>
    

    4.在Dao中注入数据源

    (1) 普通注入

    public class AccountDaoImpl implements AccountDao {
    
        private JdbcTemplate jdbcTemplate;
    
        public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
            this.jdbcTemplate = jdbcTemplate;
        }
    
        @Override
        public void addMoney(String name, Double money) {
            this.getJdbcTemplate().update("update account set money=money+? where name=?",money,name);
        }
    
        @Override
        public void minusMoney(String name, Double money) {
            this.getJdbcTemplate().update("update account set money=money-? where name=?",money,name);
        }
    }
    

    <?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"
           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">
    
        <!--加载属性文件-->
        <context:property-placeholder location="classpath:jdbc.properties"/>
    
        <!--druid-->
        <bean id="druid" class="com.alibaba.druid.pool.DruidDataSource">
            <!--key值不能和name一样,所以一般加上jdbc前缀-->
            <property name="driverClassName" value="${jdbc.driverClass}"/>
            <property name="url" value="${jdbc.url}"/>
            <property name="username" value="${jdbc.username}"/>
            <property name="password" value="${jdbc.password}"/>
        </bean>
    
        <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
            <property name="dataSource" ref="druid"/>
        </bean>
    
        <bean id="accountDao" class="com.xzh.spring.demo.AccountDaoImpl">
            <property name="jdbcTemplate" ref="jdbcTemplate"/>
        </bean>
    
    </beans>
    

    (2)继承 JdbcDaoSupport

    • 在DAO当中注入jdbc模板,要保证dao继承了JdbcDaoSupport,继承之后, 就有了datasource的set方法,就可以注入了
    public abstract class JdbcDaoSupport extends DaoSupport {
        @Nullable
        private JdbcTemplate jdbcTemplate;
    
        public JdbcDaoSupport() {
        }
    
        public final void setDataSource(DataSource dataSource) {
            if (this.jdbcTemplate == null || dataSource != this.jdbcTemplate.getDataSource()) {
                this.jdbcTemplate = this.createJdbcTemplate(dataSource);
                this.initTemplateConfig();
            }
    
        }
    	
    	// 省略后面其他方法
    	... ...
    
    }
    
    • Dao继承JdbcDaoSupport
    public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {
    
        @Override
        public void addMoney(String name, Double money) {
            this.getJdbcTemplate().update("update account set money=money+? where name=?",money,name);
        }
    
        @Override
        public void minusMoney(String name, Double money) {
            this.getJdbcTemplate().update("update account set money=money-? where name=?",money,name);
        }
    }
    
    • DAO注入JDBC模板
    <?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"
           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">
    
        <!--加载属性文件-->
        <context:property-placeholder location="classpath:jdbc.properties"/>
    
        <!--druid-->
        <bean id="druid" class="com.alibaba.druid.pool.DruidDataSource">
            <!--key值不能和name一样,所以一般加上jdbc前缀-->
            <property name="driverClassName" value="${jdbc.driverClass}"/>
            <property name="url" value="${jdbc.url}"/>
            <property name="username" value="${jdbc.username}"/>
            <property name="password" value="${jdbc.password}"/>
        </bean>
    
        <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
            <property name="dataSource" ref="druid"/>
        </bean>
    
        <bean id="accountDao" class="com.xzh.spring.demo.AccountDaoImpl">
            <property name="dataSource" ref="druid"/>
        </bean>
    
    </beans>
    

    5.创建Account业务

    public interface AccountService {
        public void transferMoney(String from,String to,Double money);
    }
    
    public class AccountServiceImpl implements AccountService{
    
        private AccountDao accountDao;
        public void setAccountDao(AccountDao accountDao) {
            this.accountDao = accountDao;
        }
    
        @Override
        public void transferMoney(String from, String to, Double money) {
            this.accountDao.minusMoney(from,money);
            this.accountDao.addMoney(to,money);
        }
    }
    

    6.配置service 交给spring 并注入dao

    <?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"
           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">
    
        <!--加载属性文件-->
        <context:property-placeholder location="classpath:jdbc.properties"/>
    
        <!--druid-->
        <bean id="druid" class="com.alibaba.druid.pool.DruidDataSource">
            <!--key值不能和name一样,所以一般加上jdbc前缀-->
            <property name="driverClassName" value="${jdbc.driverClass}"/>
            <property name="url" value="${jdbc.url}"/>
            <property name="username" value="${jdbc.username}"/>
            <property name="password" value="${jdbc.password}"/>
        </bean>
    
        <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
            <property name="dataSource" ref="druid"/>
        </bean>
    
        <bean id="accountDao" class="com.xzh.spring.demo.AccountDaoImpl">
            <property name="dataSource" ref="druid"/>
        </bean>
        
        <bean id="accountService" class="com.xzh.spring.demo.AccountServiceImpl">
            <property name="accountDao" ref="accountDao"/>
        </bean>
    
    </beans>
    

    7.测试

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration("classpath:applicationContext.xml")
    public class AccountTest {
    
        @Resource(name = "accountService")
        private AccountService accountService;
    
        @Test
        public void test(){
            accountService.transferMoney("zs","ls",100d);
        }
    }
    

    8. 遇到的问题

    在业务中如果出现异常时,数据会发生错误

    修改 AccountServiceImpl 类,手动添加一个异常

    public class AccountServiceImpl implements AccountService{
    
        private AccountDao accountDao;
        public void setAccountDao(AccountDao accountDao) {
            this.accountDao = accountDao;
        }
    
        @Override
        public void transferMoney(String from, String to, Double money) {
            this.accountDao.minusMoney(from,money);
            int i = 1/0;
            this.accountDao.addMoney(to,money);
        }
    }
    

    测试同上

    发现一方金额扣了,另一方金额却没有加。

    二、添加事务

    编程式事务

    需要手动编写代码

    步骤

    (1)配置平台事务管理器

    <!--配置事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    	<property name="dataSource" ref="druid"/>
    </bean>
    

    (2)Spring提供了事务管理的模板类

    <!--配置事务管理模板-->
    <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
    	<property name="transactionManager" ref="transactionManager"/>
    </bean>
    

    (3)在业务层注入事务管理的模板

    添加 TransactionTemplate 属性,并提供set方法

    配置文件中配置

    <bean id="accountService" class="com.xzh.springjdbc.demo2.AccountServiceImpl">
    	<property name="accountDao" ref="accountDao"/>
    	<property name="transactionTemplate" ref="transactionTemplate"/>
    </bean>
    

    (4)编写事务管理的代码

    public class AccountServiceImpl implements AccountService{
    
        private AccountDao accountDao;
    
        private TransactionTemplate transactionTemplate;
    
        public void setAccountDao(AccountDao accountDao) {
            this.accountDao = accountDao;
        }
    
        public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
            this.transactionTemplate = transactionTemplate;
        }
    
        @Override
        public void transferMoney(String from, String to, Double money) {
            this.transactionTemplate.execute(new TransactionCallbackWithoutResult() {
                @Override
                protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
                    accountDao.minusMoney(from,money);
                    int i = 1/0;
                    accountDao.addMoney(to,money);
                }
            });
        }
    }
    

    配置文件

    <?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"
           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">
    
        <!--加载属性文件-->
        <context:property-placeholder location="classpath:jdbc.properties"/>
    
        <!--druid-->
        <bean id="druid" class="com.alibaba.druid.pool.DruidDataSource">
            <!--key值不能和name一样,所以一般加上jdbc前缀-->
            <property name="driverClassName" value="${jdbc.driverClass}"/>
            <property name="url" value="${jdbc.url}"/>
            <property name="username" value="${jdbc.username}"/>
            <property name="password" value="${jdbc.password}"/>
        </bean>
    
        <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
            <property name="dataSource" ref="druid"/>
        </bean>
    
        <bean id="accountDao" class="com.xzh.springjdbc.demo2.AccountDaoImpl">
            <property name="dataSource" ref="druid"/>
        </bean>
    
        <bean id="accountService" class="com.xzh.springjdbc.demo2.AccountServiceImpl">
            <property name="accountDao" ref="accountDao"/>
            <property name="transactionTemplate" ref="transactionTemplate"/>
        </bean>
    
        <!--配置事务管理器-->
        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="druid"/>
        </bean>
    
        <!--配置事务管理模板-->
        <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
            <property name="transactionManager" ref="transactionManager"/>
        </bean>
    
    </beans>
    

    测试

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration("classpath:applicationContext.xml")
    public class AccountTest {
    
        @Resource(name = "accountService")
        private AccountService accountService;
    
        @Test
        public void test(){
            accountService.transferMoney("ls","zs",100d);
        }
    }
    

    声明式事务

    (1)XML方式声明事务管理

    1. 引入aop的开发包
    2. 配置事务管理器
    <!--配置事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    	<property name="dataSource" ref="druid"/>
    </bean>
    
    3. AOP的配置
    <!--事务增强-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
    	<tx:attributes>
    		<tx:method name="*" propagation="REQUIRED"/>
    	</tx:attributes>
    </tx:advice>
    
    <!--把增强织入对应的方法里-->
    <aop:config>
    	<aop:pointcut id="pointcut" expression="execution(* com.xzh.springjdbc.demo2.AccountServiceImpl.*(..))"/>
    	<aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut"/>
    </aop:config>
    

    业务层实现类

    public class AccountServiceImpl implements AccountService {
    
        private AccountDao accountDao;
    
        public void setAccountDao(AccountDao accountDao) {
            this.accountDao = accountDao;
        }
    
        @Override
        public void transferMoney(String from, String to, Double money) {
            accountDao.minusMoney(from, money);
            // int i = 1/0;
            accountDao.addMoney(to, money);
        }
    }
    

    总的配置

    <?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:property-placeholder location="classpath:jdbc.properties"/>
    
        <!--druid-->
        <bean id="druid" class="com.alibaba.druid.pool.DruidDataSource">
            <!--key值不能和name一样,所以一般加上jdbc前缀-->
            <property name="driverClassName" value="${jdbc.driverClass}"/>
            <property name="url" value="${jdbc.url}"/>
            <property name="username" value="${jdbc.username}"/>
            <property name="password" value="${jdbc.password}"/>
        </bean>
    
        <bean id="accountDao" class="com.xzh.springjdbc.demo2.AccountDaoImpl">
            <property name="dataSource" ref="druid"/>
        </bean>
    
        <bean id="accountService" class="com.xzh.springjdbc.demo2.AccountServiceImpl">
            <property name="accountDao" ref="accountDao"/>
        </bean>
    
        <!--配置事务管理器-->
        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="druid"/>
        </bean>
    
        <!--事务增强-->
        <tx:advice id="txAdvice" transaction-manager="transactionManager">
            <tx:attributes>
                <tx:method name="*" propagation="REQUIRED"/>
            </tx:attributes>
        </tx:advice>
    
        <!--把增强织入对应的方法里-->
        <aop:config>
            <aop:pointcut id="pointcut" expression="execution(* com.xzh.springjdbc.demo2.AccountServiceImpl.*(..))"/>
            <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut"/>
        </aop:config>
    </beans>
    

    测试

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration("classpath:applicationContext.xml")
    public class AccountTest {
    
        @Resource(name = "accountService")
        private AccountService accountService;
    
        @Test
        public void test(){
            accountService.transferMoney("ls","zs",100d);
        }
    }
    

    (2)注解方式声明事务管理

    1. 配置事务管理器
    <!--配置事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    	<property name="dataSource" ref="druid"/>
    </bean>
    
    2. 开启注解事务
    <!--开启注解 事务增强-->
    <tx:annotation-driven transaction-manager="transactionManager"/>
    
    3. 在业务层添加注解
    @Transactional
    public class AccountServiceImpl implements AccountService {
    
        private AccountDao accountDao;
    
        public void setAccountDao(AccountDao accountDao) {
            this.accountDao = accountDao;
        }
    
        @Override
        public void transferMoney(String from, String to, Double money) {
            accountDao.minusMoney(from, money);
            // int i = 1/0;
            accountDao.addMoney(to, money);
        }
    }
    

    测试同上

  • 相关阅读:
    基于51单片机的Uart串口通信协议
    基于STM32F103和Cube的输入捕获例程
    基于STM32F429和HAL库的CAN收发例程
    基于STM32F429的TFT0.96屏幕驱动
    基于STM32F429和Cube的ov2640程序
    基于STM32F429和Cube的主从定时器多通道输出固定个数的PWM波形
    基于STM32F429,Cubemx的SAI音频播放实验
    基于STM32F429的内存管理
    基于STM32F429,Cubemx的SDHC卡的基本Fatfs文件移植
    基于STM32F429的ADS1115驱动程序
  • 原文地址:https://www.cnblogs.com/xzh0717/p/10873618.html
Copyright © 2020-2023  润新知