Spring实践--spring事务:基础知识

一:基础知识介绍

1.1:事务四个特性:ACID

  • 原子性(Atomicity):事务是一个原子操做,由一系列动做组成。事务的原子性确保动做要么所有完成,要么所有失败。
  • 一致性(Consistency):一旦事务完成(无论成功仍是失败),系统必须确保它所建模的业务处于一致的状态,而不会是部分完成部分失败。在现实中的数据不该该被破坏。
  • 隔离性(Isolation):可能有许多事务会同时处理相同的数据,所以每一个事务都应该与其余事务隔离开来,防止数据损坏。
  • 持久性(Durability):一旦事务完成,不管发生什么系统错误,它的结果都不该该受到影响,这样就能从任何系统崩溃中恢复过来。一般状况下,事务的结果被写到持久化存储器中。

1.2 隔离级别错误说明

  1. 脏读:并发事务之间一个事务读取了另外一个事务还未提交的数据。
  2. 不可重复读:并发事务间一个事务屡次读取,分别读取了另外一个事务未提交和已经提交的数据,致使屡次读取的数据状态不一致。
  3. 幻读:并发事务间一个事务读取了另外一个事务已经提交的数据。与不可重复读的区别是不可重复读先后读取的是同一数据项,而幻读是一批数据。好比先后读取的数据数量不一致。

1.3 数据库四大隔离级别

  1. Serializable(串行化):可避免脏读、不可重复读、幻读的发生。
  2. Repeatable Read(可重复读):可避免脏读、不可重复读的发生。
  3. Read Committed(读已提交):可避免脏读的发生。
  4. Read unCommitted:(读未提交):最低级别,任何状况都会发生。

1.4 Spring隔离级别属性

  1. TransactionDefinition.ISOLATION_DEFAULT:这是默认值,表示使用底层数据库的默认隔离级别。对大部分数据库而言,一般这值就是TransactionDefinition.ISOLATION_READ_COMMITTED。
  2. TransactionDefinition.ISOLATION_READ_UNCOMMITTED:该隔离级别表示一个事务能够读取另外一个事务修改但尚未提交的数据。该级别不能防止脏读,不可重复读和幻读,所以不多使用该隔离级别。
  3. TransactionDefinition.ISOLATION_READ_COMMITTED:该隔离级别表示一个事务只能读取另外一个事务已经提交的数据。该级别能够防止脏读,这也是大多数状况下的推荐值。
  4. TransactionDefinition.ISOLATION_REPEATABLE_READ:该隔离级别表示一个事务在整个过程当中能够屡次重复执行某个查询,而且每次返回的记录都相同。该级别能够防止脏读和不可重复读。
  5. TransactionDefinition.ISOLATION_SERIALIZABLE:全部的事务依次逐个执行,这样事务之间就彻底不可能产生干扰,也就是说,该级别能够防止脏读、不可重复读以及幻读。可是这将严重影响程序的性能。一般状况下也不会用到该级别。

1.5 Spring事务的传播行为

事务的第一个方面是传播行为(propagation behavior)。当事务方法被另外一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在本身的事务中运行。html

Spring定义了七种传播行为:java

  1. TransactionDefinition.PROPAGATION_REQUIRED:若是当前事务存在,则加入当前事务;若是不存在,则建立一个新的事务。通常默认此项。
  2. TransactionDefinition.PROPAGATION_REQUIRES_NEW:建立一个新的事务,若是当前事务存在,则挂起当前事务。
  3. TransactionDefinition.PROPAGATION_SUPPORTS:若是当前存在事务,则加入该事物;若是事务不存在,则以无事务方式执行。
  4. TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式执行,若是当前事务存在,则挂起当前事务。
  5. TransactionDefinition.PROPAGATION_NEVER:以非事务方式执行,若是存在事务,则抛出异常。
  6. TransactionDefinition.PROPAGATION_MANDATORY:表示该方法必须在事务中运行,若是当前存在事务,则加入当前事务;若是不存在,则抛出异常。
  7. TransactionDefinition.PROPAGATION_NESTED:若是当前存在事务,则建立一个新的事务做为当前事务内嵌事务执行,嵌套的事务能够独立于当前事务进行单独地提交或回滚。若是不存在,则取该值等价于TransactionDefinition.PROPAGATION_REQUIRED,即建立一个新的事务

注:如下具体讲解传播行为的内容参考自Spring事务机制详解mysql

此@Transactional注解来自org.springframework.transaction.annotation包,而不是javax.transaction。程序员

1.6 Spring事务分类

  • 声明式事务:即为在配置文件中配置,无需程序员手动编程控制事务,也就是说数据库的事务的开启,提交都是框架帮助咱们作好的
  • 编程式事务:是须要在方法中加入Spring的事务API  例如hibernate中的  beginTransaction() commit() rollback, 更加具备细粒度,可是同时也增长了代码的倾入性!

二:核心接口

Spring事务管理的实现有许多细节,若是对整个接口框架有个大致了解会很是有利于咱们理解事务,下面经过讲解Spring的事务接口来了解Spring实现事务的具体策略。 
Spring事务管理涉及的接口的联系以下:spring

2.1 事务管理器

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

Public interface PlatformTransactionManager()...{  
    // 由TransactionDefinition获得TransactionStatus对象
    TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException; 
    // 提交
    Void commit(TransactionStatus status) throws TransactionException;  
    // 回滚
    Void rollback(TransactionStatus status) throws TransactionException;  
} 

从这里可知具体的具体的事务管理机制对Spring来讲是透明的,它并不关心那些,那些是对应各个平台须要关心的,因此Spring事务管理的一个优势就是为不一样的事务API提供一致的编程模型,如JTA、JDBC、Hibernate、JPA。下面分别介绍各个平台框架实现事务管理的机制。数据库

2.1.1 JDBC事务

若是应用程序中直接使用JDBC来进行持久化,DataSourceTransactionManager会为你处理事务边界。为了使用DataSourceTransactionManager,你须要使用以下的XML将其装配到应用程序的上下文定义中:编程

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">  
<property name="dataSource" ref="dataSource" />  
</bean>

实际上,DataSourceTransactionManager是经过调用java.sql.Connection来管理事务,然后者是经过DataSource获取到的。经过调用链接的commit()方法来提交事务,一样,事务失败则经过调用rollback()方法进行回滚。session

2.1.2 Hibernate事务

若是应用程序的持久化是经过Hibernate实习的,那么你须要使用HibernateTransactionManager。对于Hibernate3,须要在Spring上下文定义中添加以下的<bean>声明:并发

<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">  
<property name="sessionFactory" ref="sessionFactory" />  
</bean>

sessionFactory属性须要装配一个Hibernate的session工厂,HibernateTransactionManager的实现细节是它将事务管理的职责委托给org.hibernate.Transaction对象,然后者是从Hibernate Session中获取到的。当事务成功完成时,HibernateTransactionManager将会调用Transaction对象的commit()方法,反之,将会调用rollback()方法。

2.1.3 Java持久化API事务(JPA)

Hibernate多年来一直是事实上的Java持久化标准,可是如今Java持久化API做为真正的Java持久化标准进入你们的视野。若是你计划使用JPA的话,那你须要使用Spring的JpaTransactionManager来处理事务。你须要在Spring中这样配置JpaTransactionManager:

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">  
	<property name="sessionFactory" ref="sessionFactory" />  
</bean>

JpaTransactionManager只须要装配一个JPA实体管理工厂(javax.persistence.EntityManagerFactory接口的任意实现)。JpaTransactionManager将与由工厂所产生的JPA EntityManager合做来构建事务。

2.1.4 Java原生API事务

若是你没有使用以上所述的事务管理,或者是跨越了多个事务管理源(好比两个或者是多个不一样的数据源),你就须要使用JtaTransactionManager:

<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">  
	<property name="transactionManagerName" value="java:/TransactionManager" />  
</bean>

JtaTransactionManager将事务管理的责任委托给javax.transaction.UserTransaction和javax.transaction.TransactionManager对象,其中事务成功完成经过UserTransaction.commit()方法提交,事务失败经过UserTransaction.rollback()方法回滚。

2.2 基本事务属性的定义

上面讲到的事务管理器接口PlatformTransactionManager经过getTransaction(TransactionDefinition definition)方法来获得事务,这个方法里面的参数是TransactionDefinition类,这个类就定义了一些基本的事务属性。 
那么什么是事务属性呢?事务属性能够理解成事务的一些基本配置,描述了事务策略如何应用到方法上。事务属性包含了5个方面,如图所示:

而TransactionDefinition接口内容以下:

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

咱们能够发现TransactionDefinition正好用来定义事务属性,下面详细介绍一下各个事务属性。

2.2.1 Spring 事务实现原理

在应用系统调用 事务声明 的目标方法时,Spring Framework 默认使用 AOP 代理,在代码运行时生成一个代理对象,根据事务配置信息,这个代理对象决定该声明事务的目标方法是否由拦截器 TransactionInterceptor 来使用拦截,在 TransactionInterceptor 拦截时,会在目标方法开始执行以前建立并加入事务,并执行目标方法的逻辑, 最后根据执行状况是否出现异常,利用抽象事务管理器 AbstractPlatformTransactionManager 操做数据源 DataSource 提交或回滚事务。

Spring AOP 代理有 CglibAopProxy 和 JdkDynamicAopProxy 两种,以CglibAopProxy 为例,对于CglibAopProxy,须要调用其内部类的DynamicAdvisedInterceptor 的 intercept 方法。对于 JdkDynamicAopProxy,则调用其 invoke 方法。

三:遇到的问题

3.1:若是只是加了@Transactional注解,事务并不生效。

  • 事务注解就否开启:
    • xml:<tx:annotation-driver transaction-manager="transactionManager">
    • java:@EnableTransactionManagement 开启事务支持后。
  • 这个注解如果加载方法上,只能加在public的方法上,加到其余如private上不会报错,可是事务控制就不会生效。
  • mysql数据库:myisam存储引擎不支持事务,换成innodb

 

3.2 Spring事务中须要注意的问题

事务失效问题

在同一个代理对象内部,事务方法之间的直接嵌套调用,普通方法和事务方法之间的直接嵌套调用,都会形成事务异常!具体表现为某些传播行为不生效或者直接事务控制不生效。

@Service
public class DemoService {  

    @Transaction
    public void transactionMethod1()  
    {  
        op1();
        op2();
        ...
    }  
    public void commonMethod(){  
        ...
        transactionMethod1();  //照顾基础不牢的朋友,这里至关于 this.transactionMethod1(); 
        ...  
    }  


    @Transaction
    public void transactionMethod2(){  
        ...
        this.transactionMethod3();
        ...
    }  

    @Transaction(propagation= Propagation.REQUIRES_NEW)
    public void transactionMethod3()  
    {  
        op3();
        ...
    }  
} 


上面代码中,若是调用 DemoService 的 bean 对象的commonMethod ,则transactionMethod1里定义的事务将不生效(好比op2发生错误时,并不会回滚op1的操做),bean 调用 transactionMethod2时,transactionMethod2时里面调用的transactionMethod3也不会开启新的事务。

为何会这样?
上面的实现机制中讲到,AOP的实现都是经过动态代理来实现,而AOP限制了咱们只能在目标方法的开始和结束做为切点作切入处理加强。当动态代理对象最终调用的原始对象的目标方法时,并不能干预到目标方法内的方法调用行为,若是原始对象直接调用(用this.xxx方式)其余同类方法时,实际调用的是原始对象自身的方法,而不是代理类对象加强后(增长事务控制后)的方法。此时Spring对方法事务的控制(包括事务的传播行为、事务的隔离级别等)彻底失效。

如何解决?
要想解决此类问题,主要都在于原始对象在调用对象内其余方法时,不要使用this.xxx的方式直接调用,经过注入或者获取代理对象的方式,使用代理对象调用须要调用的方法。下面列举几个解决方式:

1.注入自身,使用代理对象调用

@Service
public class DemoService {  

    @Autowired
    DemoService demoService;

    @Transaction
    public void transactionMethod1() {  
        op1();
        op2();
        ...
    }  

    public void commonMethod() {  
        ...
        //this.transactionMethod1() -> demoService.transactionMethod1()  
        demoService.transactionMethod1();  
        ...  
    } 
} 



2.使用AopContext,获取当前代理对象

@Service
public class DemoService {

    @Transaction
    public void transactionMethod1()  
    {  
        op1();
        op2();
        ...
    }  

    public void commonMethod()  
    {  
        ...
        //this.transactionMethod1() -> ((DemoService)AopContext.currentProxy()).transactionMethod1();) 
        ((DemoService)AopContext.currentProxy()).transactionMethod1();
        ...  
    }  

    ...
} 


3.使用BeanFactory获取代理对象(代码略)
 

参考博客:

Spring事务配置的五种方式 : 

spring 事务传播行为实例分析

Spring事务管理(详解+实例)

SpringBoot之事务处理机制

SpringBoot使用事务和AOP

相关文章
相关标签/搜索