Spring事务处理

事务(Transaction是并发控制的单位,是用户定义的一个操做序列。这些操做要么都作,要么都不作,是一个不可分割的工做单位。html

数据库向用户提供保存当前程序状态的方法,叫事务提交(commit;当事务执行过程当中,使数据库忽略当前的状态并回到前面保存的状态的方法叫事务回滚(rollbackjava

 

事务特性(ACID)

原子性(atomicity):将事务中所作的操做捆绑成一个原子单元,即对于事务所进行的数据修改等操做,要么所有执行,要么所有不执行。spring

一致性(Consistency):事务在完成时,必须使全部的数据都保持一致状态,并且在相关数据中,全部规则都必须应用于事务的修改,以保持全部数据的完整性。事务结束时,全部的内部数据结构都应该是正确的。数据库

隔离性(Isolation):由并发事务所作的修改必须与任何其余事务所作的修改相隔离。事务查看数据时数据所处的状态,要么是被另外一并发事务修改以前的状态,要么是被另外一并发事务修改以后的状态,即事务不会查看由另外一个并发事务正在修改的数据。这种隔离方式也叫可串行性。express

持久性(Durability):事务完成以后,它对系统的影响是永久的,即便出现系统故障也是如此。segmentfault

 

事务隔离级别(Isolation Level)服务器

读操做未提交(Read Uncommitted)读取未提交的数据是容许的。说明一个事务在提交前,其变化对于其余事务来讲是可见的。这样脏读、不可重读和幻读都是容许的。当一个事务已经写入一行数据但未提交,其余事务都不能再写入此行数据;可是,任何事务均可以读任何数据。这个隔离级别使用排写锁实现。(RU模式下,一个事务能够读取到另外一个未提交(commit)的数据,致使了脏读。若是B事务回滚了,就会形成数据的不一致。RU是事务隔离级别最低的。)session

读操做已提交(Read Committed)读取未提交的数据是不容许的,它使用临时的共读锁和排写锁实现。这种隔离级别不容许脏读,但不可重读和幻读是容许的。(RC模式下,若是数据被修改,可是没有commit的时候,这种状况下取到的是修改以前的记录,这就避免了在RU模式中的脏读,若是commit以前查询了一次,在commit以后又查询了一次,就会出现两次select的数据不同,一个是更新前的数据,一个是更新后的数据,这就存在了不可重复读的问题。RC事务隔离级别是Oracle数据库的默认隔离级别.)数据结构

可重读(Repeatable Read):说明事务保证可以再次读取相同的数据而不会失败。此隔离级别不容许脏读和不可重读,但幻读会出现。(在这种隔离级别下,在一个事务中咱们可以保证可以获取到同样的数据(即便已经有其余事务修改了咱们的数据)。可是没法避免幻读,幻读简单的解释就是在数据有新增的时候,没法保证两次获得的数据不一致,可是不一样数据库对不一样的RR级别有不一样的实现,有时候或加上间隙锁来避免幻读,这是在传统的RR级别定义中会出现的。可是在innoDB中利用MVCC多版本并发控制解决了这个问题)并发

可串行化(Serializable):提供最严格的事务隔离。这个隔离级别不容许事务并行执行,只容许串行执行。这样,脏读、不可重读或幻读均可发生。

大部分状况下,不多使用彻底隔离的事务。但不彻底隔离的事务会带来以下一些问题。(全部事物串行,最高隔离级别,性能最差)

 

未彻底隔离事务可能带来的问题:

事务隔离意味着对于某一个正在运行的事务来讲,好像系统中只有这一个事务,其余并发的事务都不存在同样。

大部分状况下,不多使用彻底隔离的事务。但不彻底隔离的事务会带来以下一些问题。

更新丢失(Lost Update):两个事务都企图去更新一行数据,致使事务抛出异常退出,两个事务的更新都白费了。

脏数据(Dirty Read):若是第二个应用程序使用了第一个应用程序修改过的数据,而这个数据处于未提交状态,这时就会发生脏读。第一个应用程序随后可能会请求回滚被修改的数据,从而致使第二个事务使用的数据被损坏,即所谓的“变脏”。

不可重读(Unrepeatable Read):一个事务两次读同一行数据,但是这两次读到的数据不同,就叫不可重读。若是一个事务在提交数据以前,另外一个事务能够修改和删除这些数据,就会发生不可重读。

幻读(Phantom Read):一个事务执行了两次查询,发现第二次查询结果比第一次查询多出了一行,这多是由于另外一个事务在这两次查询之间插入了新行。针对由事务的不彻底隔离所引发的上述问题,提出了一些隔离级别,用来防范这些问题。

 

                                                              1. 1事务隔离与隔离级别的关系

隔离级别

脏读(Dirty Read)

不可重读(Unrepeatable read)

幻读(Phantom Read)

读操做未提交(Read Uncommitted)

可能

可能

可能

读操做已提交(Read Committed)

不可能

可能

可能

可重读(Repeatable Read)

不可能

不可能

可能

可串行化(Serializable)

不可能

不可能

不可能

 

事务的传播行为(Propagation)

1. 什么是事务传播行为?

事务传播行为用来描述由某一个事务传播行为修饰的方法被嵌套进另外一个方法的时事务如何传播。

用伪代码说明:

public void methodA(){
    methodB();
    //doSomething
 }
 
 @Transaction(Propagation=XXX)
 public void methodB(){
    //doSomething
 }

 

 2.Spring中七种事务传播行为

事务传播行为类型

说明

PROPAGATION_REQUIRED

若是当前没有事务,就新建一个事务,若是已经存在一个事务中,加入到这个事务中。这是 最多见的选择。

PROPAGATION_SUPPORTS

支持当前事务,若是当前没有事务,就以非事务方式执行。

PROPAGATION_MANDATORY

使用当前的事务,若是当前没有事务,就抛出异常。

PROPAGATION_REQUIRES_NEW

新建事务,若是当前存在事务,把当前事务挂起。

PROPAGATION_NOT_SUPPORTED

以非事务方式执行操做,若是当前存在事务,就把当前事务挂起。

PROPAGATION_NEVER

以非事务方式执行,若是当前存在事务,则抛出异常。

PROPAGATION_NESTED

若是当前存在事务,则在嵌套事务内执行。若是当前没有事务,则执行与 PROPAGATION_REQUIRED 相似的操做。

 当使用 PROPAGATION_NESTED 时, 底层的数据源必须基于 JDBC 3.0 ,而且实现者须要支持保存点事务机制。

 

事务属性
spring在声明事务时,用到了@Transactional(readOnly = false, propagation = Propagation.REQUIRED)。 
中间的参数readOnly,propagation咱们称为事务属性。它就是对事务的基本配置。

事务属性有五个方面:传播行为,隔离级别,事务超时时间,回滚规则,是否只读。 


由属性接口TransactionDefinition能够看到,可返回四个基本事务属性:

public interface TransactionDefinition {
    int getPropagationBehavior(); // 传播行为。
    int getIsolationLevel(); // 隔离级别。事务管理器根据它来控制另一个事务能够看到本事务内的哪些数据。
    int getTimeout();  // 事务必须在多少秒内完成。
    boolean isReadOnly(); // 事务是否只读。事务管理器可以根据这个返回值进行优化,确保事务是只读的
} 


readOnly 
    事务属性中的readOnly标志表示对应的事务应该被最优化为只读事务。这是一个最优化提示 。在一些状况下,一些事务策略可以起到显著的最优化效果,例如在使用Object/Relational映射工具 (如:Hibernate或TopLink)时避免dirty checking(试图“刷新”)。

Timeout

     在事务属性中还有定义“timeout”值的选项,指定事务超时为几秒。在JTA中,这将被简单地传递到J2EE服务器的事务协调程序,并据此获得相应的解释。

例子:

ServiceA {

    void methodA() {
        try {
        //savepoint
        ServiceB.methodB();
        } 
        catch (SomeException) {
        // 执行其余业务, 如 ServiceC.methodC();
        }
    }

}

1: PROPAGATION_REQUIRED

加入当前正要执行的事务不在另一个事务里,那么就起一个新的事务

例如

        ServiceB.methodB的事务级别定义为PROPAGATION_REQUIRED

        ServiceA.methodA已经起了事务,这时调用ServiceB.methodB,ServiceB.methodB就加入ServiceA.methodA的事务内部,就再也不起新的事务。ServiceA.methodA没有在事务中,这时调用ServiceB.methodB,

        ServiceB.methodB就会为本身分配一个事务。

        在ServiceA.methodA或者在ServiceB.methodB内的任何地方出现异常,事务都会被回滚。即便ServiceB.methodB的事务已经被提交,可是ServiceA.methodA在接下来fail要回滚,ServiceB.methodB也要回滚

 

2: PROPAGATION_SUPPORTS

若是当前在事务中,即以事务的形式运行,若是当前再也不一个事务中,那么就以非事务的形式运行

3: PROPAGATION_MANDATORY

必须在一个事务中运行。也就是说,他只能被一个父事务调用。不然,他就要抛出异常

4: PROPAGATION_REQUIRES_NEW

例如

        ServiceA.methodA的事务级别为PROPAGATION_REQUIRED,ServiceB.methodB的事务级别为PROPAGATION_REQUIRES_NEW,

        当调用ServiceB.methodB的时候,ServiceA.methodA所在的事务就会挂起,ServiceB.methodB会起一个新的事务,等待ServiceB.methodB的事务完成之后,他才继续执行。

        PROPAGATION_REQUIRES_NEW与PROPAGATION_REQUIRED 的事务区别在于事务的回滚程度:

                由于ServiceB.methodB和ServiceA.methodA两个不一样的事务。若是ServiceB.methodB已经提交,那么ServiceA.methodA失败回滚,ServiceB.methodB是不会回滚的。若是ServiceB.methodB失败回滚,

                若是他抛出的异常被ServiceA.methodA捕获,ServiceA.methodA事务仍然可能提交。

 

5: PROPAGATION_NOT_SUPPORTED

当前不支持事务

例如:

  ServiceA.methodA的事务级别是PROPAGATION_REQUIRED ,而ServiceB.methodB的事务级别是PROPAGATION_NOT_SUPPORTED ,

  调用ServiceB.methodB时,ServiceA.methodA的事务挂起,而以非事务的状态运行完,再继续ServiceA.methodA的事务。

6: PROPAGATION_NEVER

不能在事务中运行

假设ServiceA.methodA的事务级别是PROPAGATION_REQUIRED,  而ServiceB.methodB的事务级别是PROPAGATION_NEVER ,

那么ServiceB.methodB就要抛出异常了。

7: PROPAGATION_NESTED

理解Nested的关键是savepoint。他与PROPAGATION_REQUIRES_NEW的区别是,PROPAGATION_REQUIRES_NEW另起一个事务,将会与他的父事务相互独立,

而Nested的事务和他的父事务是相依的,他的提交是要等和他的父事务一块提交的。也就是说,若是父事务最后回滚,他也要回滚的。

而Nested事务的好处是他有一个savepoint。

Spring事务处理

  Spring配置文件中关于事务配置老是由三个组成部分,分别是DataSource、TransactionManager和代理机制这三部分,不管哪一种配置方式,通常变化的只是代理机制这部分。

    DataSource、TransactionManager这两部分只是会根据数据访问方式有所变化,好比使用Hibernate进行数据访问 时,DataSource实际为SessionFactory,TransactionManager的实现为 HibernateTransactionManager。

具体以下图:

     

 

第一种方式:每一个Bean都有一个代理

 

1.    <?xml version="1.0" encoding="UTF-8"?>  
2.    <beans xmlns="http://www.springframework.org/schema/beans"  
3.        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
4.        xmlns:context="http://www.springframework.org/schema/context"  
5.        xmlns:aop="http://www.springframework.org/schema/aop"  
6.        xsi:schemaLocation="http://www.springframework.org/schema/beans   
7.               http://www.springframework.org/schema/beans/spring-beans-2.5.xsd  
8.               http://www.springframework.org/schema/context  
9.               http://www.springframework.org/schema/context/spring-context-2.5.xsd  
10.               http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">  
11.      
12.        <bean id="sessionFactory"    
13.                class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">    
14.            <property name="configLocation" value="classpath:hibernate.cfg.xml" />    
15.            <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />  
16.        </bean>    
17.      
18.        <!-- 定义事务管理器(声明式的事务) -->    
19.        <bean id="transactionManager"  
20.            class="org.springframework.orm.hibernate3.HibernateTransactionManager">  
21.            <property name="sessionFactory" ref="sessionFactory" />  
22.        </bean>  
23.          
24.        <!-- 配置DAO -->  
25.        <bean id="userDaoTarget" class="com.bluesky.spring.dao.UserDaoImpl">  
26.            <property name="sessionFactory" ref="sessionFactory" />  
27.        </bean>  
28.          
29.        <bean id="userDao"    
30.            class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">    
31.               <!-- 配置事务管理器 -->    
32.               <property name="transactionManager" ref="transactionManager" />       
33.            <property name="target" ref="userDaoTarget" />    
34.             <property name="proxyInterfaces" value="com.bluesky.spring.dao.GeneratorDao" />  
35.            <!-- 配置事务属性 -->    
36.            <property name="transactionAttributes">    
37.                <props>    
38.                    <prop key="*">PROPAGATION_REQUIRED</prop>  
39.                </props>    
40.            </property>    
41.        </bean>    
42.    </beans>

 第二种方式:全部Bean共享一个代理基类

<?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-2.5.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-2.5.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">

    <bean id="sessionFactory" 
            class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> 
        <property name="configLocation" value="classpath:hibernate.cfg.xml" /> 
        <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />
    </bean> 

    <!-- 定义事务管理器(声明式的事务) --> 
    <bean id="transactionManager"
        class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>
   
    <bean id="transactionBase" 
            class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean" 
            lazy-init="true" abstract="true"> 
        <!-- 配置事务管理器 --> 
        <property name="transactionManager" ref="transactionManager" /> 
        <!-- 配置事务属性 --> 
        <property name="transactionAttributes"> 
            <props> 
                <prop key="*">PROPAGATION_REQUIRED</prop> 
            </props> 
        </property> 
    </bean>   
  
    <!-- 配置DAO -->
    <bean id="userDaoTarget" class="com.bluesky.spring.dao.UserDaoImpl">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>
   
    <bean id="userDao" parent="transactionBase" > 
        <property name="target" ref="userDaoTarget" />  
    </bean>
</beans>

第三种方式:使用拦截器

<?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-2.5.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-2.5.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">

    <bean id="sessionFactory" 
            class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> 
        <property name="configLocation" value="classpath:hibernate.cfg.xml" /> 
        <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />
    </bean> 

    <!-- 定义事务管理器(声明式的事务) --> 
    <bean id="transactionManager"
        class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean> 
  
    <bean id="transactionInterceptor" 
        class="org.springframework.transaction.interceptor.TransactionInterceptor"> 
        <property name="transactionManager" ref="transactionManager" /> 
        <!-- 配置事务属性 --> 
        <property name="transactionAttributes"> 
            <props> 
                <prop key="*">PROPAGATION_REQUIRED</prop> 
            </props> 
        </property> 
    </bean>
     
    <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> 
        <property name="beanNames"> 
            <list> 
                <value>*Dao</value>
            </list> 
        </property> 
        <property name="interceptorNames"> 
            <list> 
                <value>transactionInterceptor</value> 
            </list> 
        </property> 
    </bean> 
 
    <!-- 配置DAO -->
    <bean id="userDao" class="com.bluesky.spring.dao.UserDaoImpl">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>
</beans>

第四种方式:使用tx标签配置的拦截器

<?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-2.5.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-2.5.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">

    <context:annotation-config />
    <context:component-scan base-package="com.bluesky" />

    <bean id="sessionFactory" 
            class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> 
        <property name="configLocation" value="classpath:hibernate.cfg.xml" /> 
        <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />
    </bean> 

    <!-- 定义事务管理器(声明式的事务) --> 
    <bean id="transactionManager"
        class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>

    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="*" propagation="REQUIRED" />
        </tx:attributes>
    </tx:advice>
   
    <aop:config>
        <aop:pointcut id="interceptorPointCuts"
            expression="execution(* com.bluesky.spring.dao.*.*(..))" />
        <aop:advisor advice-ref="txAdvice"
            pointcut-ref="interceptorPointCuts" />       
    </aop:config>     
</beans>

第五种方式:全注解

<?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-2.5.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-2.5.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">

    <context:annotation-config />
    <context:component-scan base-package="com.bluesky" />

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

    <bean id="sessionFactory" 
            class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> 
        <property name="configLocation" value="classpath:hibernate.cfg.xml" /> 
        <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />
    </bean> 

    <!-- 定义事务管理器(声明式的事务) --> 
    <bean id="transactionManager"
        class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>
   
</beans>

 

此时在DAO上需加上@Transactional注解,以下:

package com.bluesky.spring.dao;

import java.util.List;

import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.stereotype.Component;

import com.bluesky.spring.domain.User;

@Transactional
@Component("userDao")
public class UserDaoImpl extends HibernateDaoSupport implements UserDao {

    public List<User> listUsers() {
        return this.getSession().createQuery("from User").list();
    }
    
    
}

Spring事务处理原理

问题

       一、当JPA框架对数据库进行操做的时候,是从那里获取Connection?

       二、jdbc对事务的配置,好比事务的开启,提交以及回滚是在哪里设置的?

       三、Spring是经过aop拦截切面的全部须要进行事务管理的业务处理方法,那如何获取业务处理方法里面对数据库操做的事务呢?

解答

      一、既然在JPA的框架里面配置了datasource,那天然会从这个datasource里面去得到链接。

      二、jdbc的事务配置是在Connection对消里面有对应的方法,好比setAutoCommit,commit,rollback这些方法就是对事务的操做。

      三、Spring须要操做事务,那必需要对Connection来进行设置。Spring的AOP能够拦截业务处理方法,而且也知道业务处理方法里面的 DAO操做的JAP框架是从datasource里面获取Connection对象,那么Spring须要对当前拦截的业务处理方法进行事务控制,那     必然 须要获得他内部的Connection对象。总体的结构图以下:

            

 

附上文章:

一、MySQL事务隔离级别和Spring事务关系介绍

https://blog.csdn.net/hellozhxy/article/details/81081187

二、Spring service本类中方法调用另外一个方法事务不生效问题

https://blog.csdn.net/qq_16675313/article/details/79955136

三、Spring为内部方法新起一个事务,此处应有坑。

https://www.cnblogs.com/yougewe/p/7466677.html

4.Spring事务传播行为和具体示例详解

https://segmentfault.com/a/1190000013341344

相关文章
相关标签/搜索