Spring的编程式事务和声明式事务详解

入口(了解一些基本概念)

Spring事务属性(事务的属性有哪些?)

我们都知道事务有开始,保存点,提交,回滚,隔离级别等属性。那么Spring对于事务属性定义有哪些呢?通过TransactionDefinition接口我们可以了解到:

public interface TransactionDefinition{
int getIsolationLevel();
int getPropagationBehavior();
int getTimeout();
boolean isReadOnly();
} 

获取隔离级别

获取传播特性

获取超时

获取是否只读

事务隔离级别

隔离离别也是通过TransactionDefinition接口定义的,代表并发事务的隔离程度。

隔离级别 描述
TransactionDefinition.ISOLATION_DEFAULT 这是默认值,表示使用底层数据库的默认隔离级别。对大部分数据库而言,通常这值就是TransactionDefinition.ISOLATION_READ_COMMITTED
TransactionDefinition.ISOLATION_READ_UNCOMMITTED 该隔离级别表示一个事务可以读取另一个事务修改但还没有提交的数据。该级别不能防止脏读和不可重复读,因此很少使用该隔离级别
TransactionDefinition.ISOLATION_READ_COMMITTED 该隔离级别表示一个事务只能读取另一个事务已经提交的数据。该级别可以防止脏读,这也是大多数情况下的推荐值
TransactionDefinition.ISOLATION_REPEATABLE_READ 该隔离级别表示一个事务在整个过程中可以多次重复执行某个查询,并且每次返回的记录都相同。即使在多次查询之间有新增的数据满足该查询,这些新增的记录也会被忽略。该级别可以防止脏读和不可重复读。
TransactionDefinition.ISOLATION_SERIALIZABLE 所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。

* 事务传播行为

所谓事务的传播行为是指,如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为。在TransactionDefinition定义中包括了如下几个表示传播行为的常量.

传播行为 描述
TransactionDefinition.PROPAGATION_REQUIRED 如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务
TransactionDefinition.PROPAGATION_REQUIRES_NEW 创建一个新的事务,如果当前存在事务,则把当前事务挂起
TransactionDefinition.PROPAGATION_SUPPORTS 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行
TransactionDefinition.PROPAGATION_NOT_SUPPORTED 以非事务方式运行,如果当前存在事务,则把当前事务挂起
TransactionDefinition.PROPAGATION_NEVER 以非事务方式运行,如果当前存在事务,则抛出异常
TransactionDefinition.PROPAGATION_MANDATORY 如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常
TransactionDefinition.PROPAGATION_NESTED 如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED

* 事务超时

指定事务的最大运行时间。使用int指定,单位是秒。

* 事务的只读属性

事务的只读属性是指,对事务性资源进行只读操作或者是读写操作。所谓事务性资源就是指那些被事务管理的资源,比如数据源、 JMS 资源,以及自定义的事务性资源等等。如果确定只对事务性资源进行只读操作,那么我们可以将事务标志为只读的,以提高事务处理的性能。在 TransactionDefinition 中以 boolean 类型来表示该事务是否只读。

* 事务的回滚规则

默认出现RuntimeException就会回滚。如果没有抛出任何异常,或者抛出了已检查异常,则仍然提交事务。这通常也是大多数开发者希望的处理方式,也是 EJB 中的默认处理方式。(这里个人理解的是已检查异常,是我们定义的checkedException,包括我们自定义的异常和调用方法捕获的异常)

Spring事务的三个基本类

Spring 框架中,涉及到事务管理的 API 大约有100个左右,其中最重要的有三个:TransactionDefinition、PlatformTransactionManager、TransactionStatus。所谓事务管理,其实就是“按照给定的事务规则来执行提交或者回滚操作”。“给定的事务规则”就是用 TransactionDefinition 表示的,“按照……来执行提交或者回滚操作”便是用 PlatformTransactionManager 来表示,而 TransactionStatus 用于表示一个运行着的事务的状态。打一个不恰当的比喻,TransactionDefinition 与 TransactionStatus 的关系就像程序和进程的关系。

TransactionDefinition 定义事务的属性

TransactionStatus 定义事务的状态

public interface TransactionStatus{
boolean isNewTransaction();
void setRollbackOnly();
boolean isRollbackOnly();
} 

PlatformTransactionManager 就是各种事务平台的实现接口

Public interface PlatformTransactionManager{
TransactionStatus getTransaction(TransactionDefinition definition)
throws TransactionException;
void commit(TransactionStatus status)throws TransactionException;
void rollback(TransactionStatus status)throws TransactionException;
} 

所以我们现在可以向下,spring的事务真正实现是PlatformTransactionManager的实现类,通过各种参数来符合TransactionDefinition和TransactionStatus的要求,最后根据我们编程的或者声明的进行事务管理。

根据底层框架的不同(稍后我们看一下DataSourceTransactoinManager和HibernateTransactionManager的代码,主要做了什么),Spring(或者其他框架)提供主要的实现如下:

DataSourceTransactionManager: 适合JDBC和ibatis

HibernateTransactionManager: 适合hibernate

JpaTransactionManager: 适用于使用JPA进行数据持久化操作的情况(更底层的一些)

适用于使用JPA进行数据持久化操作的情况

到这里基本概念终于结束了,我们可以介绍两种类型的事务管理了

编程式事务管理

首先我们回想一下不适用spring事务管理时,hibernate事务的管理是怎么样的? 大概是我们手动获取session,获取transaction,开始transaction,提交或者回滚,关闭session

那么我们使用spring的管理之后,事务本身控制还是交给持久框架自己管理。知识spring像一个代理人一样,你告诉它,它之后转化后告诉底层框架。

看个实际例子吧:

基于底层API的编程式事务管理

public class BankServiceImpl implements BankService {
  private BankDao bankDao;
  private TransactionDefinition txDefinition; // transaction定义是哪个
  private PlatformTransactionManager txManager; //具体使用的txmanager
  ......
  public boolean transfer(Long fromId, Long toId, double amount) {
  //这里获取事务状态
  TransactionStatus txStatus = txManager.getTransaction(txDefinition);
  boolean result = false;
  try {
  result = bankDao.transfer(fromId, toId, amount);
  //提交事务
  txManager.commit(txStatus);
  } catch (Exception e) {
  result = false;
  //回滚
  txManager.rollback(txStatus);
  System.out.println("Transfer Error!");
  }
  return result;
  }
}

对应的xml文件:

<bean id="bankService" class="footmark.spring.core.tx.programmatic.origin.BankServiceImpl">
  <property name="bankDao" ref="bankDao"/>
  <property name="txManager" ref="transactionManager"/>
  <property name="txDefinition">
    <bean class="org.springframework.transaction.support.DefaultTransactionDefinition">
  <property name="propagationBehaviorName" value="PROPAGATION_REQUIRED"/>
  </bean>
  </property>
</bean>

但是这种写法和我们不适用spring的有何不同呢,最多是将事务层次提高了service层,不限于dao层,所以Spring做了改进:
基于TransactionTemplate的编程式事务管理

TransactionTemplate的execute方法提供一个内部匿名类,用来我们写transaction代码,然后提供一个transactionStatus的参数,这样你可以控制回滚。这样一来,我们就不用写任何关于事务API的代码了。格式大概是 Boolean b = transactionTempate.execute(new TransactionCallBack() { 执行方法(TransactionStatus transactionStatus){} },当执行完成后返回一个boolean的值. 还有一个方法,就是不提供返回结果的。

public class BankServiceImpl implements BankService {
  private BankDao bankDao;
  private TransactionTemplate transactionTemplate;
  ......
  public boolean transfer(final Long fromId, final Long toId, final double amount) {
    //调用一个回调函数
    return (Boolean) transactionTemplate.execute(new TransactionCallback(){
  public Object doInTransaction(TransactionStatus status) {
    Object result;
    try {
      result = bankDao.transfer(fromId, toId, amount);
    } catch (Exception e) {
    status.setRollbackOnly();
    result = false;
    System.out.println("Transfer Error!");
    }
    return result;
    }
    });
  }
}

对应的XML:

<bean id="bankService"
class="footmark.spring.core.tx.programmatic.template.BankServiceImpl">
  <property name="bankDao" ref="bankDao"/>
  <property name="transactionTemplate" ref="transactionTemplate"/>
</bean>

从结果来看,好像还是不够简单和清晰。下面我们来看声明式事务管理,也是比较推荐的方式

声明式事务管理

Spring的声明式事务管理是基于AOP的,在方法前和后加上切点,用来打开事务和提交/回滚事务。

基于TransactionInterceptor的管理

最初,Spring 提供了 TransactionInterceptor 类来实施声明式事务管理功能

<beans...>
  ......
  <bean id="transactionInterceptor"
  class="org.springframework.transaction.interceptor.TransactionInterceptor">
    <property name="transactionManager" ref="transactionManager"/>
    <property name="transactionAttributes">
      <props>
        //指定了方法,可以使用通配符
        <prop key="transfer">PROPAGATION_REQUIRED</prop>
      </props>
    </property>
  </bean>
  <bean id="bankServiceTarget"
  class="footmark.spring.core.tx.declare.origin.BankServiceImpl">
  <property name="bankDao" ref="bankDao"/>
  </bean>
  <bean id="bankService"
  class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="target" ref="bankServiceTarget"/>
    <property name="interceptorNames">
      <list>
        <idref bean="transactionInterceptor"/>
      </list>
    </property>
  </bean>
  ......
</beans>

首先,我们配置了一个 TransactionInterceptor 来定义相关的事务规则,他有两个主要的属性:一个是 transactionManager,用来指定一个事务管理器,并将具体事务相关的操作委托给它;另一个是 Properties 类型的 transactionAttributes 属性,它主要用来定义事务规则,该属性的每一个键值对中,键指定的是方法名,方法名可以使用通配符,而值就表示相应方法的所应用的事务属性。

指定事务属性的取值有较复杂的规则,这在 Spring 中算得上是一件让人头疼的事。具体的书写规则如下:

传播行为 [,隔离级别] [,只读属性] [,超时属性] [不影响提交的异常] [,导致回滚的异常]

基于 TransactionProxy… 的声明式事务管理

前面的声明式事务虽然好,但是却存在一个非常恼人的问题:配置文件太多。

为了缓解这个问题,Spring 为我们提供了 TransactionProxyFactoryBean,用于将TransactionInterceptor 和 ProxyFactoryBean 的配置合二为一

<beans......>
  ......
  <bean id="bankServiceTarget"
  class="footmark.spring.core.tx.declare.classic.BankServiceImpl">
    <property name="bankDao" ref="bankDao"/>
  </bean>
  <bean id="bankService"
  class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
    <property name="target" ref="bankServiceTarget"/>
    <property name="transactionManager" ref="transactionManager"/>
    <property name="transactionAttributes">
      <props>
        <prop key="transfer">PROPAGATION_REQUIRED</prop>
      </props>
    </property>
  </bean>
  ......
</beans>

这样子是减少了proxy的代码,但是每个service还是需要一个配置。所以我们可以使用自动代理的配置,这样子就减少了大量的配置。也应该是最常用的。

!-- Spring事务管理 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource" />
</bean>
<!-- 配置事务的传播特性 -->
<bean id="baseTransactionProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean" abstract="true" >
  <property name="transactionManager" ref="transactionManager" />
  <property name="transactionAttributes">
    <props>
      <prop key="add*">PROPAGATION_REQUIRED</prop>
      <prop key="edit*">PROPAGATION_REQUIRED</prop>
      <prop key="remove*">PROPAGATION_REQUIRED</prop>
      <prop key="insert*">PROPAGATION_REQUIRED</prop>
      <prop key="update*">PROPAGATION_REQUIRED</prop>
      <prop key="del*">PROPAGATION_REQUIRED</prop>
      <prop key="*">readOnly</prop>
    </props>
  </property>
</bean>

基于 命名空间的声明式事务管理

前面两种声明式事务配置方式奠定了 Spring 声明式事务管理的基石。在此基础上,Spring 2.x 引入了 命名空间,结合使用 命名空间,带给开发人员配置声明式事务的全新体验,配置变得更加简单和灵活。另外,得益于 命名空间的切点表达式支持,声明式事务也变得更加强大。

具体例子:

<beans......>
  ......
  <bean id="bankService"
  class="footmark.spring.core.tx.declare.namespace.BankServiceImpl">
    <property name="bankDao" ref="bankDao"/>
  </bean>
  <tx:advice id="bankAdvice" transaction-manager="transactionManager">
    <tx:attributes>
      <tx:method name="transfer" propagation="REQUIRED"/>
    </tx:attributes>
  </tx:advice>
  <aop:config>
    <aop:pointcut id="bankPointcut" expression="execution(* *.transfer(..))"/>
    <aop:advisor advice-ref="bankAdvice" pointcut-ref="bankPointcut"/>
  </aop:config>
  ......
</beans>

@Transactional 的声明式事务管理

除了基于命名空间的事务配置方式,Spring 2.x 还引入了基于 Annotation 的方式,具体主要涉及@Transactional 标注。@Transactional 可以作用于接口、接口方法、类以及类方法上。当作用于类上时,该类的所有 public 方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义。

具体例子:

@Transactional(propagation = Propagation.REQUIRED)
public boolean transfer(Long fromId, Long toId, double amount) {
  return bankDao.transfer(fromId, toId, amount);
}

但是使用这种我们就必须启用tx的annotation:

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

附录

DataSourceTransactionManager(类主要的方法,就不看原代码了)

设置和获取DataSource

获取transaction

transaction是否存在

开始transaction

暂停,释放连接connection

恢复暂停的连接

提交

回滚

仅回滚

清理

HibernateTransactionManager

转换异常

开始事务

清理

提交

获取事务

恢复

回滚

仅回滚

暂停

获取数据源

获取Entity的Interceptor

获取SessionFactory

是否存在事务

是否预先提交

总结

本文有关Spring的编程式事务和声明式事务详解的介绍就到这里,希望对大家有所帮助。有什么问题可以随时留言,小编会及时回复大家。在此也非常感谢大家对本站的支持!

(0)

相关推荐

  • spring声明式事务管理解析

    前沿:通过对spring事务管理有了比较深入学习,本文将不做实例,而是指定具体的类和配置文件进行讲解. 本文内容: 1.了解什么是声明式事务? 2.声明式事务管理分别有哪几种? 3.这几种事务管理之间的区别是什么? 一.什么是声明式事务? 声明式事务(declarative transaction management)是spring提供的对程序事务管理的方式之一.Spring的声明式事务就是采用声明的方式来处理事务,用在Spring配置文件中声明式的处理事务来代替代码式的处理事务.这样的好处是

  • spring声明式事务解析

    一.spring声明式事务 1.1 spring的事务管理器 spring没有直接管理事务,而是将管理事务的责任委托给JTA或相应的持久性机制所提供的某个特定平台的事务实现.spring容器负责事物的操作,spring容器充当切面,事务的方法称为增强处理,生成的代理对象的方法就是目标方法+增强也就是crud+事务程序员只用做crud的操作,也就是目标方法和声明哪些方法应该在事务中运行. Spring提供了许多内置事务管理器实现: DataSourceTransactionManager:位于or

  • springboot开启声明式事务的方法

    springboot开启事务很简单,只需要一个注解@Transactional 就可以了.因为在springboot中已经默认对jpa.jdbc.mybatis开启了事事务,引入它们依赖的时候,事物就默认开启.当然,如果你需要用其他的orm,比如beatlsql,就需要自己配置相关的事物管理器. 准备阶段 以上一篇文章的代码为例子,即springboot整合mybatis,上一篇文章是基于注解来实现mybatis的数据访问层,这篇文章基于xml的来实现,并开启声明式事务. 环境依赖 在pom文件

  • 完美解决Spring声明式事务不回滚的问题

    疑问,确实像往常一样在service上添加了注解 @Transactional,为什么查询数据库时还是发现有数据不一致的情况,想想肯定是事务没起作用,出现异常的时候数据没有回滚.于是就对相关代码进行了一番测试,结果发现一下踩进了两个坑,确实是事务未回滚导致的数据不一致. 下面总结一下经验教训: Spring事务的管理操作方法 编程式的事务管理 实际应用中很少使用 通过使用TransactionTemplate 手动管理事务 声明式的事务管理 开发中推荐使用(代码侵入最少) Spring的声明式事

  • Spring的编程式事务和声明式事务详解

    入口(了解一些基本概念) Spring事务属性(事务的属性有哪些?) 我们都知道事务有开始,保存点,提交,回滚,隔离级别等属性.那么Spring对于事务属性定义有哪些呢?通过TransactionDefinition接口我们可以了解到: public interface TransactionDefinition{ int getIsolationLevel(); int getPropagationBehavior(); int getTimeout(); boolean isReadOnly

  • Spring编程式和声明式事务实例讲解小结

    Spring事务管理 Spring支持两种方式的事务管理: 编程式事务管理: 通过Transaction Template手动管理事务,实际应用中很少使用, 使用XML配置声明式事务: 推荐使用(代码侵入性最小),实际是通过AOP实现 实现声明式事务的四种方式: 基于 TransactionInterceptor 的声明式事务: Spring 声明式事务的基础,通常也不建议使用这种方式,但是与前面一样,了解这种方式对理解 Spring 声明式事务有很大作用. 基于 TransactionProx

  • Spring如何基于xml实现声明式事务控制

    一.pom.xml <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM

  • SpringBoot 注解事务声明式事务的方式

    springboot 对新人来说可能上手比springmvc要快,但是对于各位从springmvc转战到springboot的话,有些地方还需要适应下,尤其是xml配置.我个人是比较喜欢注解➕xml是因为看着方便,查找方便,清晰明了.但是xml完全可以使用注解代替,今天就扒一扒springboot中事务使用注解的玩法. springboot的事务也主要分为两大类,一是xml声明式事务,二是注解事务,注解事务也可以实现类似声明式事务的方法,关于注解声明式事务,目前网上搜索不到合适的资料,所以在这里

  • Spring AOP事务管理的示例详解

    目录 转账案例-环境搭建 步骤1:准备数据库表 步骤2:创建项目导入jar包 步骤3:根据表创建模型类 步骤4:创建Dao接口 步骤5:创建Service接口和实现类 步骤6:添加jdbc.properties文件 步骤7:创建JdbcConfig配置类 步骤8:创建MybatisConfig配置类 步骤9:创建SpringConfig配置类 步骤10:编写测试类 事务管理 转账案例-环境搭建 步骤1:准备数据库表 之前我们在整合Mybatis的时候已经创建了这个表,可以直接使用 create

  • MySQL事务管理的作用详解

    目录 1.为何使用事务管理 2.数据库事务的原理 3.什么是事务 3.1 事务的特性ACID 3.2 事务的并发问题 3.3 隔离级别 4.Spring事务管理 1.为何使用事务管理 可以保证数据的完整性.事务(Transaction),就是将一组SQL语句放在同一批次内去执行,如果一个SQL语句出错,则该批次内 的所有SQL都将被取消执行. 例子: 转账为例. 金庸向张无忌转账1000元.----在数据库中修改两个账号的余额. 发生意外情况,则出现金庸减钱成功,而张无忌加钱失败. 如何解决?

  • MySQL索引与事务定义到使用详解

    目录 1.索引的本质 2.索引的使用 2.1查看索引 2.2创建索引 2.3删除索引 3.索引的数据结构 3.1B树 3.2B+树 4.事务 4.1事物的回滚(rollback) 4.2事务的四大特性(ACID) 4.2.1 原子性 4.2.2 一致性 4.2.3 持久性 4.2.4 隔离性 5.并发引起的问题 5.1 "读脏数据" 5.2 "不可重复读" 5.3 "幻读" 6.MySQL的隔离级别 6.1 read uncommitted 6.

  • spring对JDBC和orm的支持实例详解

    简介 Spring提供的DAO(数据访问对象)支持主要的目的是便于以标准的方式使用不同的数据访问技术,如JDBC,Hibernate或者JDO等.它不仅可以让你方便地在这些持久化技术间切换, 而且让你在编码的时候不用考虑处理各种技术中特定的异常. 一致的异常层次 Spring提供了一种方便的方法,把特定于某种技术的异常,如SQLException, 转化为自己的异常,这种异常属于以 DataAccessException 为根的异常层次.这些异常封装了原始异常对象,这样就不会有丢失任何错误信息的

  • Spring AOP面向切面编程实现原理方法详解

    1. 什么是AOP AOP (Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现在不修改源代码的情况下,给程序动态统一添加功能的一种技术,可以理解成动态代理.是Spring框架中的一个重要内容.利用 AOP 可以对业务逻辑的各个部分进行隔离,使业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高开发的效率 2. Spring AOP ①. AOP 在Spring中的作用 提供声明式事务:允许用户自定义切面 ②. AOP 的基本概

  • Spring 整合 Hibernate 时启用二级缓存实例详解

    Spring 整合 Hibernate 时启用二级缓存实例详解 写在前面: 1. 本例使用 Hibernate3 + Spring3: 2. 本例的查询使用了 HibernateTemplate: 1. 导入 ehcache-x.x.x.jar 包: 2. 在 applicationContext.xml 文件中找到 sessionFactory 相应的配置信息并在设置 hibernateProperties 中添加如下代码: <!-- 配置使用查询缓存 --> <prop key=&q

随机推荐