Spring总结之事务

Spring事务

1)定义

    事务是指多个操作单元组成的集合,多个操作单元是整体不可分割的,要么都成功,要么都不成功。必须遵守四个原则(ACID)

      ●原子性(Atomicity):即事务是不可分割的最小工作单元,事务内的操作要么全做,要么全不做;

      ●一致性(Consistency):在事务执行前数据库的数据处于正确的状态,而事务执行完成后数据库的数据还是应该处于正确的状态,即数据完整性约束没有被破坏;如银行转帐,A转帐给B,必须保证A的钱一定转给B,一定不会出现A的钱转了但B没收到,否则数据库的数据就处于不一致(不正确)的状态。

      ●隔离性(Isolation):并发事务执行之间互不影响,在一个事务内部的操作对其他事务是不产生影响,这需要事务隔离级别来指定隔离性;

      ●持久性(Durability):事务一旦执行成功,它对数据库的数据的改变必须是永久的,不会因比如遇到系统故障或断电造成数据不一致或丢失。

2)Spring事务隔离级别

    spring有五大隔离级别,其在TransactionDefinition接口中定义。看源码可知,其默isolation_default(底层数据库默认级别),其他四个隔离级别跟数据库隔离级别一致。

    ●ISOLATION_DEFAULT:用底层数据库的默认隔离级别,数据库管理员设置什么就是什么

     ●ISOLATION_READ_UNCOMMITTED(未提交读):最低隔离级别、事务未提交前,就可被其他事务读取(会出现幻读、脏读、不可重复读)

     ●ISOLATION_READ_COMMITTED(提交读):一个事务提交后才能被其他事务读取到(该隔离级别禁止其他事务读取到未提交事务的数据、所以还是会造成幻读、不可重复读)、sql server默认级别

     ●ISOLATION_REPEATABLE_READ(可重复读):可重复读,保证多次读取同一个数据时,其值都和事务开始时候的内容是一致,禁止读取到别的事务未提交的数据(该隔离基本可防止脏读,不可重复读(重点在修改),但会出现幻读(重点在增加与删除))(MySql默认级别,更改可通过set transaction isolation level 级别)

     ●ISOLATION_SERIALIZABLE(序列化):代价最高最可靠的隔离级别(该隔离级别能防止脏读、不可重复读、幻读)

丢失更新:两个事务同时更新一行数据,最后一个事务的更新会覆盖掉第一个事务的更新,从而导致第一个事务更新的数据丢失,这是由于没有加锁造成的;

幻读:同样的事务操作过程中,不同时间段多次(不同事务)读取同一数据,读取到的内容不一致(一般是行数变多或变少)。

脏读:一个事务读取到另外一个未提及事务的内容,即为脏读。

不可重复读:同一事务中,多次读取内容不一致(一般行数不变,而内容变了)。

幻读与不可重复读的区别:幻读的重点在于插入与删除,即第二次查询会发现比第一次查询数据变少或者变多了,以至于给人一种幻象一样,而不可重复读重点在于修改,即第二次查询会发现查询结果比第一次查询结果不一致,即第一次结果已经不可重现了。

数据库隔离级别越高,执行代价越高,并发执行能力越差,因此在实际项目开发使用时要综合考虑,为了考虑并发性能一般使用提交读隔离级别,它能避免丢失更新和脏读,尽管不可重复读和幻读不能避免,但可以在可能出现的场合使用悲观锁或乐观锁来解决这些问题。

3)Spring事务管理

    (1)编程式事务管理       

        Spring 提供的事务模版类:org.springframework.transaction.support.TransactionTemplate

        事务管理器:org.springframework.jdbc.datasource.DataSourceTransactionManager

<?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:p="http://www.springframework.org/schema/p"  
    xmlns:aop="http://www.springframework.org/schema/aop"   
    xmlns:context="http://www.springframework.org/schema/context"  
    xmlns:jee="http://www.springframework.org/schema/jee"  
    xmlns:tx="http://www.springframework.org/schema/tx"  
    xsi:schemaLocation="    
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd  
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd  
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd  
        http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.0.xsd  
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">
         
   <context:property-placeholder location="jdbc.properties"/>
   <!-- 数据源 -->
   <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
         <property name="driverClassName" value="${jdbc.driverClassName}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
   </bean>
    <!-- jdbcTemplate -->
   <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
         <property name="dataSource" ref="dataSource"></property>
   </bean>
   <!-- jdbc 事务管理器-->
   <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
         <property name="dataSource" ref="dataSource"></property>
   </bean>
   <!-- TransactionTemplate -->
   <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
         <property name="transactionManager" ref="transactionManager"></property>
   </bean>
   
   <!-- dao -->
   <bean id="userDao" class="top.ruandb.dao.impl.UserDaoImpl">
         <property name="jdbcTemplate" ref="jdbcTemplate"></property>
   </bean>
   <bean id="userService" class="top.ruandb.service.impl.UserServiceImpl">
         <property name="userDao" ref="userDao"></property>
         <property name="transactionTemplate" ref="transactionTemplate"></property>
   </bean>
    
</beans>
public class UserServiceImpl implements UserServiceI {
    private TransactionTemplate transactionTemplate;
    private UserDaoI userDao;
    public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
        this.transactionTemplate = transactionTemplate;
    }
    public void setUserDao(UserDaoI userDao) {
        this.userDao = userDao;
    }
        //addUser()和updateUser()在同一个事务中
    @Override
    public void addUser() {
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus status) {
                userDao.addUser();
                userDao.updateUser();
            }
        });
    }
}

(2)声明式事务管理

        使用 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:p="http://www.springframework.org/schema/p"  
    xmlns:aop="http://www.springframework.org/schema/aop"   
    xmlns:context="http://www.springframework.org/schema/context"  
    xmlns:jee="http://www.springframework.org/schema/jee"  
    xmlns:tx="http://www.springframework.org/schema/tx"  
    xsi:schemaLocation="    
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd  
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd  
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd  
        http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.0.xsd  
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">
         
   <context:property-placeholder location="jdbc.properties"/>
   <!-- 数据源 -->
   <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
         <property name="driverClassName" value="${jdbc.driverClassName}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
   </bean>
    <!-- jdbcTemplate -->
   <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
         <property name="dataSource" ref="dataSource"></property>
   </bean>
   <!-- jdbc 事务管理器-->
   <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
         <property name="dataSource" ref="dataSource"></property>
   </bean>
   <!-- 配置事务通知 -->
   <tx:advice id="txAdvice" transaction-manager="transactionManager">
         <tx:attributes>  
            <tx:method name="insert*" propagation="REQUIRED" />  
            <tx:method name="update*" propagation="REQUIRED" />  
            <tx:method name="edit*" propagation="REQUIRED" />  
            <tx:method name="save*" propagation="REQUIRED" />  
            <tx:method name="add*" propagation="REQUIRED" />  
            <tx:method name="new*" propagation="REQUIRED" />  
            <tx:method name="set*" propagation="REQUIRED" />  
            <tx:method name="remove*" propagation="REQUIRED" />  
            <tx:method name="delete*" propagation="REQUIRED" />  
            <tx:method name="change*" propagation="REQUIRED" />  
            <tx:method name="get*" propagation="REQUIRED" read-only="true" />  
            <tx:method name="find*" propagation="REQUIRED" read-only="true" />  
            <tx:method name="load*" propagation="REQUIRED" read-only="true" />  
            <tx:method name="*" propagation="REQUIRED" read-only="true" />  
        </tx:attributes>  
   </tx:advice>
   <aop:config>
         <aop:pointcut expression="execution(* top.ruandb.service.*.*(..))" id="serviceMethod"/>
         <aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethod"/>
   </aop:config>
  
   <!-- dao -->
   <bean id="userDao" class="top.ruandb.dao.impl.UserDaoImpl">
         <property name="jdbcTemplate" ref="jdbcTemplate"></property>
   </bean>
   <bean id="userService" class="top.ruandb.service.impl.UserServiceImpl">
         <property name="userDao" ref="userDao"></property>
   </bean>
    
</beans>
        使用注解配置声明式事务( @Transactional);

<?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:p="http://www.springframework.org/schema/p"  
    xmlns:aop="http://www.springframework.org/schema/aop"   
    xmlns:context="http://www.springframework.org/schema/context"  
    xmlns:jee="http://www.springframework.org/schema/jee"  
    xmlns:tx="http://www.springframework.org/schema/tx"  
    xsi:schemaLocation="    
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd  
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd  
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd  
        http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.0.xsd  
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">
         
   <context:property-placeholder location="jdbc.properties"/>
   <!-- 数据源 -->
   <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
         <property name="driverClassName" value="${jdbc.driverClassName}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
   </bean>
    <!-- jdbcTemplate -->
   <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
         <property name="dataSource" ref="dataSource"></property>
   </bean>
   <!-- jdbc 事务管理器-->
   <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
         <property name="dataSource" ref="dataSource"></property>
   </bean>
   <!-- 注解声明式事务,织入事务管理器 -->
  <tx:annotation-driven transaction-manager="transactionManager"/>
  
   <!-- dao -->
   <bean id="userDao" class="top.ruandb.dao.impl.UserDaoImpl">
         <property name="jdbcTemplate" ref="jdbcTemplate"></property>
   </bean>
   <bean id="userService" class="top.ruandb.service.impl.UserServiceImpl">
         <property name="userDao" ref="userDao"></property>
   </bean>
    
</beans>
@Transactional
public class UserServiceImpl implements UserServiceI {
    private UserDaoI userDao;
    public void setUserDao(UserDaoI userDao) {
        this.userDao = userDao;
    }
    @Override
    public void addUser() {
                userDao.addUser();
                userDao.updateUser();
    }
}

4)事务的传播行为

    事务传播行为:Spring 中,当一个 service 方法调用另外一个 service 方法的时候,因为每个 service 方法都有事

务,这时候就出现了事务的嵌套;由此,就产生了事务传播行为;

    在 Spring 中,通过配置 Propagation,来定义事务传播行为;

        PROPAGATION_REQUIRED--支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。

        PROPAGATION_SUPPORTS--支持当前事务,如果当前没有事务,就以非事务方式执行。

        PROPAGATION_MANDATORY--支持当前事务,如果当前没有事务,就抛出异常。

        PROPAGATION_REQUIRES_NEW--新建事务,如果当前存在事务,把当前事务挂起。

        PROPAGATION_NOT_SUPPORTED--以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。

        PROPAGATION_NEVER--以非事务方式执行,如果当前存在事务,则抛出异常。

原文地址:https://www.cnblogs.com/jnba/p/10832722.html