Spring事务总结(合集)

Spring事务总结(合集)

面试被问到,下面总结一下:

什么是事务?

事务(Transaction),一般是指要做的或所做的事情。在计算机术语中是指访问并可能更新数据库中各种数据项的一个程序执行单元(unit)。事务通常由高级数据库操纵语言或编程语言(如SQL,C++或Java)书写的用户程序的执行所引起,并用形如begin transaction和end transaction语句(或函数调用)来界定。事务由事务开始(begin transaction)和事务结束(end transaction)之间执行的全体操作组成

为什么要事务?

事务是为解决数据安全操作提出的,事务控制实际上就是控制数据的安全访问。

事务的四个特性(ACID):

  1. 原子性(Atomicity):
    事务最基本的操作单元,要么全部成功,要么全部失败,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚到事务开始前的状态,就像这个事务从来没有执行过一样。
  2. 一致性(Consistency):
    事务的一致性指的是在一个事务执行之前和执行之后数据库都必须处于一致性状态。如果事务成功地完成,那么系统中所有变化将正确地应用,系统处于有效状态。如果在事务中出现错误,那么系统中的所有变化将自动地回滚,系统返回到原始状态。
  3. 隔离性(Isolation):
    指的是在并发环境中,当不同的事务同时操纵相同的数据时,每个事务都有各自的完整数据空间。由并发事务所做的修改必须与任何其他并发事务所做的修改隔离。事务查看数据更新时,数据所处的状态要么是另一事务修改它之前的状态,要么是另一事务修改它之后的状态,事务不会查看到中间状态的数据。
  4. 持久性(Durability):
    指的是只要事务成功结束,它对数据库所做的更新就必须永久保存下来。即使发生系统崩溃,重新启动数据库系统后,数据库还能恢复到事务成功结束时的状态

Spring事务的传播行为

事务传播行为(传播特性)就是多个事务方法调用时,如何定义方法间事务的传播。
Spring定义了7中传播行为(传播特性):

  1. PROPAGATION_REQUIRED:如果存在一个事务,则支持当前事务。如果没有事务则开启
  2. PROPAGATION_SUPPORTS:如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行
  3. PROPAGATION_MANDATORY:如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常
  4. PROPAGATION_REQUIRES_NEW:总是开启一个新的事务。如果一个事务已经存在,则将这个存在的事务挂起
  5. PROPAGATION_NOT_SUPPORTED:总是非事务地执行,并挂起任何存在的事务
  6. PROPAGATION_NEVER: 总是非事务地执行,如果存在一个活动事务,则抛出异常
  7. PROPAGATION_NESTED:如果一个活动的事务存在,则运行在一个嵌套的事务中. 如果没有活动事务, PROPAGATION_REQUIRED 属性执行

总结如下:
事务传播特性

Spring事务的事务隔离级别:

数据库事务并发问题:
假设现在有两个事务:Transaction01和Transaction02并发执行。

  1. 脏读
    ①Transaction01将某条记录的AGE值从20修改为30。
    ②Transaction02读取了Transaction01更新后的值:30。
    ③Transaction01回滚,AGE值恢复到了20。
    ④Transaction02读取到的30就是一个无效的值。
  2. 不可重复读
    ①Transaction01读取了AGE值为20。
    ②Transaction02将AGE值修改为30。
    ③Transaction01再次读取AGE值为30,和第一次读取不一致。
  3. 幻读
    ①Transaction01读取了STUDENT表中的一部分数据。
    ②Transaction02向STUDENT表中插入了新的行。
    ③Transaction01读取了STUDENT表时,多出了一些行。

隔离级别:
数据库系统必须具有隔离并发运行各个事务的能力,使它们不会相互影响,避免各种并发问题。一个事务与其他事务隔离的程度称为隔离级别。SQL标准中规定了多种事务隔离级别,不同隔离级别对应不同的干扰程度,隔离级别越高,数据一致性就越好,但并发性越弱。

  1. 读未提交:READ UNCOMMITTED
    允许Transaction01读取Transaction02未提交的修改。
  2. 读已提交:READ COMMITTED
    要求Transaction01只能读取Transaction02已提交的修改。
  3. 可重复读:REPEATABLE READ
    确保Transaction01可以多次从一个字段中读取到相同的值,即Transaction01执行期间禁止其它事务对这个字段进行更新。
  4. 串行化:SERIALIZABLE
    确保Transaction01可以多次从一个表中读取到相同的行,在Transaction01执行期间,禁止其它事务对这个表进行添加、更新、删除操作。可以避免任何并发问题,但性能十分低下。

各个隔离级别解决并发问题的能力见下表:

隔离级别脏读不可重复读幻读
READ UNCOMMITTED
READ COMMITTED
REPEATABLE READ
SERIALIZABLE

各种数据库产品对事务隔离级别的支持程度

OracleMySQL
READ UNCOMMITTED×
READ COMMITTED√(默认)
REPEATABLE READ×√(默认)
SERIALIZABLE

Spring容器事务实现

声明式和编程式

  • 当需要用到事务操作的地方很少的时候,那么就可以使用编程方式 TransactionTemplate,它不会建立很多事务代理。
  • 如果程序中用到大力的事务操作,声明式事务方式更适合,它使得事务管理和业务逻辑分离。

编程式事务管理
编程式事务管理对基于 POJO 的应用来说是唯一选择。我们需要在代码中调用beginTransaction()、commit()、rollback()等事务管理相关的方法,这就是编程式事务管理。

声明式事务管理
声明式事务管理只需要用到@Transactional 注解和@EnableTransactionManagement。它是基于 Spring AOP 实现的,并且通过注解实现,实现起来简单,对原有代码没有入侵性。

Spring事务管理涉及的接口及其联系:
spring事务管理接口

  • Spring并不直接管理事务,而是提供了多种事务管理器,他们将事务管理的职责委托给Hibernate或者JTA等持久化机制所提供的相关平台框架的事务来实现。
  • Spring事务管理器的接口是org.springframework.transaction.PlatformTransactionManager,通过这个接口,Spring为各个平台如JDBC、Hibernate等都提供了对应的事务管理器,但是具体的实现就是各个平台自己的事情了。

声明式事务管理具体实现
(1)基于 TransactionProxyFactoryBean的声明式事务管理

<?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:mvc="http://www.springframework.org/schema/mvc"
    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-4.0.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-aop-4.2.xsd
        ">
    <context:property-placeholder location="classpath:jdbc.properties"/>
    
    <!-- 注册数据源 C3P0 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"  >
         <property name="driverClass" value="${jdbc.driverClass}"></property>
         <property name="jdbcUrl"  value="${jdbc.url}"></property>
         <property name="user"  value="${jdbc.username}"></property>
         <property name="password" value="${jdbc.password}"></property>
    </bean>
    
    <bean id="accountDao" class="transaction.test2.dao.AccountDaoImpl">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    
    <bean id="stockDao" class="transaction.test2.dao.StockDaoImpl">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    
    <bean id="buyStockService" class="transaction.test2.service.BuyStockServiceImpl">
        <property name="accountDao" ref="accountDao"></property>
        <property name="stockDao" ref="stockDao"></property>
    </bean>
    
    
    <!-- 事务管理器 -->
    
    <bean id="myTracnsactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    
    <!-- 事务代理工厂 -->
    <!-- 生成事务代理对象 -->
    <bean id="serviceProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
        <property name="transactionManager" ref="myTracnsactionManager"></property>
        <property name="target" ref="buyStockService"></property>
        <property name="transactionAttributes">
            <props>
                <!-- 主要 key 是方法   
                    ISOLATION_DEFAULT  事务的隔离级别
                    PROPAGATION_REQUIRED  传播行为
                -->
                <prop key="add*">ISOLATION_DEFAULT,PROPAGATION_REQUIRED</prop>
                <!-- -Exception 表示发生指定异常回滚,+Exception 表示发生指定异常提交 -->
                <prop key="buyStock">ISOLATION_DEFAULT,PROPAGATION_REQUIRED,-BuyStockException</prop>
            </props>
        </property>
    </bean>
</beans>  

(2)基于 @Transactional 的声明式事务管理

public class BuyStockServiceImpl implements BuyStockService{
 
    private AccountDao accountDao;
    private StockDao stockDao;
    
    @Transactional(isolation=Isolation.DEFAULT,propagation=Propagation.REQUIRED)
    @Override
    public void addAccount(String accountname, double money) {
        accountDao.addAccount(accountname,money);
        
    }
 
    @Transactional(isolation=Isolation.DEFAULT,propagation=Propagation.REQUIRED)
    @Override
    public void addStock(String stockname, int amount) {
        stockDao.addStock(stockname,amount);
        
    }
 
    public BuyStockServiceImpl() {
        // TODO Auto-generated constructor stub
    }
    
    @Transactional(isolation=Isolation.DEFAULT,propagation=Propagation.REQUIRED,rollbackFor=BuyStockException.class)
    @Override
    public void buyStock(String accountname, double money, String stockname, int amount) throws BuyStockException {
        boolean isBuy = true;
        accountDao.updateAccount(accountname, money, isBuy);
        if(isBuy==true){
            throw new BuyStockException("购买股票发生异常");
        }
            stockDao.updateStock(stockname, amount, isBuy);
        
    }
 
    public AccountDao getAccountDao() {
        return accountDao;
    }
 
    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }
 
    public StockDao getStockDao() {
        return stockDao;
    }
 
    public void setStockDao(StockDao stockDao) {
        this.stockDao = stockDao;
    }
    
}
    <context:property-placeholder location="classpath:jdbc.properties"/>
    
    <!-- 注册数据源 C3P0 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"  >
         <property name="driverClass" value="${jdbc.driverClass}"></property>
         <property name="jdbcUrl"  value="${jdbc.url}"></property>
         <property name="user"  value="${jdbc.username}"></property>
         <property name="password" value="${jdbc.password}"></property>
    </bean>
    
    <bean id="accountDao" class="transaction.test3.dao.AccountDaoImpl">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    
    <bean id="stockDao" class="transaction.test3.dao.StockDaoImpl">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    
    <bean id="buyStockService" class="transaction.test3.service.BuyStockServiceImpl">
        <property name="accountDao" ref="accountDao"></property>
        <property name="stockDao" ref="stockDao"></property>
    </bean>
    
    
    <!-- 事务管理器 -->
    <bean id="myTracnsactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    
    <!-- 启用事务注解 -->
    <tx:annotation-driven transaction-manager="myTracnsactionManager"/>

可以看出,使用@Transactional注解的方式配置文件要简单的多,将事务交给事务注解驱动。它有个缺陷是他会把所有的连接点都作为切点将事务织入进去,显然只需要在buyStock()方法织入事务即可。下面看看最后一种实现,它就可以精准的织入到指定的连接点

(3)基于Aspectj AOP配置事务

public class BuyStockServiceImpl implements BuyStockService{
 
    private AccountDao accountDao;
    private StockDao stockDao;
    
    @Override
    public void addAccount(String accountname, double money) {
        accountDao.addAccount(accountname,money);
        
    }
 
    @Override
    public void addStock(String stockname, int amount) {
        stockDao.addStock(stockname,amount);
        
    }
 
    public BuyStockServiceImpl() {
        // TODO Auto-generated constructor stub
    }
    
    @Override
    public void buyStock(String accountname, double money, String stockname, int amount) throws BuyStockException {
        boolean isBuy = true;
        accountDao.updateAccount(accountname, money, isBuy);
        if(isBuy==true){
            throw new BuyStockException("购买股票发生异常");
        }
            stockDao.updateStock(stockname, amount, isBuy);
        
    }
 
    public AccountDao getAccountDao() {
        return accountDao;
    }
 
    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }
 
    public StockDao getStockDao() {
        return stockDao;
    }
 
    public void setStockDao(StockDao stockDao) {
        this.stockDao = stockDao;
    }
    
}
    <context:property-placeholder location="classpath:jdbc.properties"/>
    
    <!-- 注册数据源 C3P0 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"  >
         <property name="driverClass" value="${jdbc.driverClass}"></property>
         <property name="jdbcUrl"  value="${jdbc.url}"></property>
         <property name="user"  value="${jdbc.username}"></property>
         <property name="password" value="${jdbc.password}"></property>
    </bean>
    
    <bean id="accountDao" class="transaction.test4.dao.AccountDaoImpl">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    
    <bean id="stockDao" class="transaction.test4.dao.StockDaoImpl">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    
    <bean id="buyStockService" class="transaction.test4.service.BuyStockServiceImpl">
        <property name="accountDao" ref="accountDao"></property>
        <property name="stockDao" ref="stockDao"></property>
    </bean>
    
    
    <!-- 事务管理器 -->
    <bean id="myTracnsactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    
    <tx:advice id="txAdvice" transaction-manager="myTracnsactionManager">
        <tx:attributes>
            <!-- 为连接点指定事务属性 -->
            <tx:method name="add*" isolation="DEFAULT" propagation="REQUIRED"/>
            <tx:method name="buyStock" isolation="DEFAULT" propagation="REQUIRED" rollback-for="BuyStockException"/>
        </tx:attributes>
    </tx:advice>
    
    <aop:config>
        <!-- 切入点配置 -->
        <aop:pointcut expression="execution(* *..service.*.*(..))" id="point"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="point"/>
    </aop:config>

参考

事务-百度百科

Spring事务管理之几种方式实现事务

Spring怎么实现事务?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值