1、Spring中事务管理的API

事务是指逻辑上要么全部成功、要么全部失败的一组操作。例如用户A给用户B转账,则用户A账户余额减少、用户B账户增加这两个操作就是一组事务,必须全部成功或失败撤回操作,不能出现A账户余额减少,B增加失败的情况。事务具有如下几个特性:

  • 原子性:事务的操作不可分割
  • 一致性:账户A的减少和B的增加一起发生
  • 隔离性:在多个事务操作时,彼此之间互不干扰
  • 持久性:事务提交到数据库之后永久的改变

spring提供了三个接口用于实现事务的管理,在进行事务管理时,spring首先会读取TransactionDefinition中隔离、传播、超时等事务定义信息,然后PlatformTransactionManager会根据这些信息进行事务管理,然后将产生的事务状态保存在TransactionStatus中

TransactionDefinition

事务定义信息接口中ISOLATION开头的常量用于定义事务的隔离级别、PROPAGATION开头定义事务传播行为、TIMEOUT_DEFAULT定义超时时间。

隔离级别用于解决多个事务提交时可能出现的脏读、不可重复读、

  • 脏读:A事务读取B事务尚未提交的数据,此时如果B事务发生错误并执行回滚操作,那么A事务读取到的数据就是脏数据。
  • 不可重复读:一个事务读取了另一个事务更新(update)前、后的数据,导致前后两次读取数据不一致
  • 幻读:一个事务读取时,另一个事务进行插入(insert),导致读取到了之前没有的记录

事务的隔离级别有如下四种类型,如果为DEFAULT则使用后端数据库默认的隔离级别,例如MySQL使用的是repeatable_read,Oracle数据库使用的是read_committed级别

READ_UNCOMMITED 允许你读取还未提交的改变了的数据。可能导致脏、幻、不可重复读
READ_COMMITTED 允许在并发事努已经提交后读取。可防止脏读,但幻读和不可重复读仍可发生
REPEATABLE_READ 对相同字段的多次读取是一致的,除非数据被事务本身改变。可防止脏、不可重复读,但幻读仍可能发生。
SERIALIZABLE 完全服从ACID的隔离级别,通过完全锁定事务中涉及的数据表来确保不发生脏、幻、不可重复读,但执行效率最低

事务的传播行为用于解决业务层方法互相调用时如何传递事务的问题。例如方法a和b中都用到了事务T,那么a在调用b时是新建一个事务T还是使用b中的事务T呢?有如下七种传播方式

PROPAGATION_REQUIRED 支持当前事务,如果不存在就新建一个
PROPAGATION_SUPPORTS 支持当前事务,如果不存在,就不使用事务
PROPAGATION_MANDATORY 支持当前事务,如果不存在,抛出异常
PROPAGATION_REQUIRES_NEW 如果有事务存在,挂起当前事劳,创建一个新的事务
PROPAGATION_NOT_SUPPORTED 以非事务方式运行,如果有事务存在,挂起当前事务
PROPAGATION_NEVER 以非事务方式运行,如果有事务存在,抛出异常
PROPAGATION_NESTED 如果当前事务存在,则嵌套执行事务

PlatformTransactionManager

spring为不同的持久层框架提供了相应的PlatformTransactionManager接口实现,spring JDBC和MyBatis对应DataSourceTransactionManager、Hibernate对应HibernateTransactionManager,还有JPA、Jdo、JTA等,不同的持久层使用不同的实现类

org.springframework.jdbc.datasource.DataSourceTransaction.Manager
Spring JDBC或iBatis逬行持久化数据时使用
org.springframework.orm.hibernate3.HibernateTransactionManager
Hibernate3.0版本进行持久化数据时使用
org.springframework.orm.jpa.JpaTransactionManager
JPA进行持久化时使用
org.springframework.jdo.JdoTransaction Manager
当持久化机制是Jdo时使用
org.springframework.transactionjta.JtaTransactionManager
JTA管理事务,在一个事务跨越多 个资源时必须使用

TransactionStatus

用于记录事务是否完成、是否产生保存点、是否可回滚等状态。

2、编程式事务管理

在Spring中使用事务有两种方式,第一种是通过手动编写来实现一个事务。

如下左图所示为一个用户账户的数据表,通过事务管理实现根据id对账户的money进行转账,以实现一个账户money减少的同时另一个增加。

   

1、引入jar包。上面右图所示为项目的目录结构,lib文件夹中包含了项目依赖的jar包,包括Spring基础包commons-logging、spring-core、spring-beans、spring-context、spring-expression、spring-test,以及连接数据库的spring-jdbc、mysql-connector,事务管理spring-tx,此外要用到c3p0对数据库连接池进行管理,除了引入c3p0包之外还需要引入mchange-commons包才可以使用。

2、配置数据源连接,在jdbc.properties文件中完成数据库连接的配置

jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test
jdbc.username=root
jdbc.password=1234

接着在配置文件spring-transaction.xml中引入properties并将属性注入c3p0的配置中,完成dataSource的配置

    <!-- 引入属性文件jdbc.properties --><context:property-placeholder location="jdbc.properties"/><!-- 配置c3p0连接池 --><bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"><property name="driverClass" value="${jdbc.driverClass}"/><property name="jdbcUrl" value="${jdbc.url}"/><property name="user" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/></bean>

3、实现数据对象的DAO类,在AccountDao类中通过继承spring的JdbcDaoSupport类,可以使用JdbcTemplate中的update()方法完成对数据库的更新操作。由于JdbcDaoSupport需要DataSource获取数据库的连接,因此在AccountDao的Bean配置中通过属性注入c3p0数据源dataSource

package com.transaction;import org.springframework.jdbc.core.support.JdbcDaoSupport;public class AccountDao extends JdbcDaoSupport {public void transferIn(int id,double money){String sql="UPDATE customers SET money = money + ? WHERE id =?";this.getJdbcTemplate().update(sql,money,id);        //使用JdbDaoSupport类的方法操作数据库}public void transferOut(int id,double money){String sql="UPDATE customers SET money = money - ? WHERE id =?";this.getJdbcTemplate().update(sql,money,id);}
}
    <!-- DAO类 --><bean class="com.transaction.AccountDao" id="accountDao"><!-- SpringJdbc需要注入连接池DataSource作为参数来初始化 --><property name="dataSource" ref="dataSource"/></bean>

4、实现事务操作Service类。在AccountService类中完成事务操作,spring编程式的事务管理通过Spring的TransactionTemplate类来实现,将一个事务操作放在其execute()方法内完成,execute()需要传入一个TransactionCallback类作为参数,这里采用匿名内部类的方式实现。在内部类中doIntransaction()方法中执行事务操作,通过调用DAO层的方法完成具体数据库操作

package com.transaction;import org.springframework.transaction.support.TransactionTemplate;public class AccountService {private AccountDao accountDao;private TransactionTemplate transactionTemplate;public void setAccountDao(AccountDao accountDao) {this.accountDao = accountDao;}public void setTransactionTemplate(TransactionTemplate transactionTemplate) {this.transactionTemplate = transactionTemplate;}public void transfer(int inAccount, int outAccount, double money) {//使用spring的TransactionTemplate进行事务操作transactionTemplate.execute(new TransactionCallback<Object>() {@Overridepublic Object doInTransaction(TransactionStatus transactionStatus) {accountDao.transferIn(inAccount, money);int i = 1/0;            //除数为0会抛出异常accountDao.transferOut(outAccount, money);return null;}});}
}

AccountService类中用到了accountDao和transactionTemplate两个属性,因此在配置Bean时需要注入两个bean对象。accountDao类是之前创建的,那么transactionTemplate类哪里来的呢?transactionTemplate是DataSourceTransactionManager创建的,之前提到它是PlatformTransactionManager接口用于spring jdbc的一个实现类。所以需要先创建一个transactionManager的bean,而要创建它还需要注入DataSource作为属性。

     <!-- 事务管理器 --><bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource"/></bean><!-- 事务管理模板 --><bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate"><property name="transactionManager" ref="transactionManager"/></bean><!-- 业务层类 --><bean id="accountService" class="com.transaction.AccountService"><property name="accountDao" ref="accountDao"/><property name="transactionTemplate" ref="transactionTemplate"/></bean>

项目的数据流动如下:

最后测试AccountService类,调用accountService对象的transfer()方法从id为2的向1转账50。由于在事务中进行了除以0的操作,事务执行失败并且回滚,数据库操作不生效

class AccountServiceTest {@org.junit.jupiter.api.Testvoid transfer() {ApplicationContext appCtx=new ClassPathXmlApplicationContext("spring-transaction.xml");AccountService accountService= appCtx.getBean("accountService",AccountService.class);accountService.transfer(2,1,50);}
}

3、声明式事务管理

Spring基于声明式的事务管理是利用AOP的思想,因此需要额外引入spring-aop和aopalliance两个jar包。由于SpringAOP的使用有三种方式,所以声明式事务管理也有三种:通过spring的TransactionProxy、使用<aop>标签、使用注解的方式

基于TransactionProxy的方式

在accountService类的基础上通过TransactionProxyBeanFactory产生代理类serviceProxy来对事务进行管理,由于是通过AOP引入的方式,所以原来的accountService类不需要增加transactionTemplate

public class AccountService {private AccountDao accountDao;public void setAccountDao(AccountDao accountDao) {this.accountDao = accountDao;}public void transfer(int inAccount, int outAccount, double money) {accountDao.transferIn(inAccount, money);int i = 1/0;            //除数为0会抛出异常accountDao.transferOut(outAccount, money);}
}

xml文件中也不需要对TransactionTemplate进行配置,只需要保留TransactionManager,但是需要新配置代理类serviceProxy,代理配置时需要注入属性--代理目标对象、事务管理器和事务属性。在transactionAttributes中通过<prop>键值对标签配置具体transfer()方法的事务属性,其中PROPAGATION_开头的是传播行为,ISOLATION_开头的是隔离级别,+开头的定义发生哪些异常后事务不回滚,-开头定义发生哪些异常回滚。

    <!-- 业务层代理 --><bean id="serviceProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"><!-- 代理目标 --><property name="target" ref="accountService"/><!-- 注入事务管理器 --><property name="transactionManager" ref="transactionManager"/><!-- 配置事务属性 --><property name="transactionAttributes"><props><prop key="transfer">PROPAGATION_REQUIRED,ISOLATION_REPEATABLE_READ,+ArithmeticException</prop></props></property></bean>

在使用时通过serviceProxy获取代理Bean,通过增强后的代理调用transfer()

    @Testvoid transfer() {ApplicationContext appCtx=new ClassPathXmlApplicationContext("spring-transaction.xml");AccountService accountService= appCtx.getBean("serviceProxy",AccountService.class);accountService.transfer(1,2,50);}

基于AspectJ的<aop>标签

由于上面的方法需要为每一个事务对象单独配置一个代理类,较为繁琐,所以实际中不常用。使用AspectJ不产生代理类,而是直接织入到accountService中。由于需要使用aspectJ进行织入,所以需要引入aspectjweaver.jar包。此外在xml配置文件中使用到了<tx>和<aop>标签,需要在<beans>声明。然后通过<tx>标签定义事务通知,其中的<tx:method>中可以对具体事务方法进行配置,包括隔离级别isolation、传播属性propagation、不回滚的异常。在<aop:config>标签值对切面进行配置,包括切入点和通知。

<?xml version="1.0" encoding="UTF-8"?>
<!-- 声明tx、aop等标签 -->
<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:tx="http://www.springframework.org/schema/tx"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">......<!-- 配置事务AOP的通知--><tx:advice id="serviceAdvice" transaction-manager="transactionManager"><tx:attributes><tx:method name="transfer" isolation="REPEATABLE_READ" propagation="REQUIRED" no-rollback-for="ArithmeticException"/></tx:attributes></tx:advice><!-- 配置切面 --><aop:config><!-- 配置切入点 --><aop:pointcut id="transferPointcut" expression="execution(* com.transaction.AccountService.*(..))"/><!-- 配置通知 --><aop:advisor advice-ref="serviceAdvice" pointcut-ref="transferPointcut"/></aop:config></beans>

由于不产生代理类,可以直接使用accountService对象

    @Testvoid transfer() {ApplicationContext appCtx=new ClassPathXmlApplicationContext("spring-transaction.xml");AccountService accountService= appCtx.getBean("accountService",AccountService.class);accountService.transfer(1,2,50);}

基于注解的方式

通过注解来使用spring事务管理代码更为简洁,只需要在xml配置文件中配置事务管理器并开启事务注解即可

    <!-- 事务管理器 --><bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource"/></bean>   <!-- 开启基于注解的事务管理 --><tx:annotation-driven transaction-manager="transactionManager"/>

接着在具体要使用到事务的类上添加@Transactional注解即可。在注解中可以指定隔离级别、传播属性、回滚/不回滚异常等属性

package com.transaction;import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;@Transactional(isolation = Isolation.REPEATABLE_READ,propagation = Propagation.REQUIRED,rollbackFor = java.lang.ArithmeticException.class)
public class AccountService {private AccountDao accountDao;public void setAccountDao(AccountDao accountDao) {this.accountDao = accountDao;}public void transfer(int inAccount, int outAccount, double money) {accountDao.transferIn(inAccount, money);int i = 1/0;            //除数为0会抛出异常accountDao.transferOut(outAccount, money);}
}

Spring中进行事务管理的两种方式相关推荐

  1. spring支持事务管理的两种方式

    转载:https://blog.csdn.net/bao19901210/article/details/41724355 事物管理对于企业应用来说是至关重要的,好使出现异常情况,它也可以保证数据的一 ...

  2. spring中AOP动态代理的两种方式

    AOP动态代理的两种方式 Spring AOP动态代理的方式(spring的AOP默认是JDK Proxy) 浅谈这两种动态代理 JDK的动态代理,需要有实现接口 动态代理--JDK Proxy ⚫ ...

  3. spring事务管理的两种方式

    一.注解式事务 1.注解式事务在平时的开发中使用的挺多,工作的两个公司中看到很多项目使用了这种方式,下面看看具体的配置demo. 2.事务配置实例 (1).spring+mybatis 事务配置 &l ...

  4. Spring中的事务管理详解

    在这里主要介绍Spring对事务管理的一些理论知识,实战方面参考上一篇博文: http://www.cnblogs.com/longshiyVip/p/5061547.html 1. 事务简介: 事务 ...

  5. Spring(四)——AOP、Spring实现AOP、Spring整合Mybatis、Spring中的事务管理

    文章目录 1. 什么是AOP 2. 使用Spring实现AOP 2.1 使用Spring的API 接口实现 2.2 自定义实现 2.3 使用注解实现 3. 整合MyBatis 3.1 MyBatis- ...

  6. spring事务管理器的作用_【面试必问】Spring中的事务管理详解

    在这里主要介绍Spring对事务管理的一些理论知识,实战方面参考上一篇博文: http://www.cnblogs.com/longshiyVip/p/5061547.html 1. 事务简介: 事务 ...

  7. 基于Spring中的事务管理机制

    什么是事务? 通俗理解,事务其实就是一系列指令的集合. 为什么要使用事务管理? 我们在实际业务场景中,经常会遇到数据频繁修改读取的问题.在同一时刻,不同的业务逻辑对同一个表数据进行修改,这种冲突很可能 ...

  8. Spring加载properties文件的两种方式

    2019独角兽企业重金招聘Python工程师标准>>> 在项目中如果有些参数经常需要修改,或者后期可能需要修改,那我们最好把这些参数放到properties文件中,源代码中读取pro ...

  9. 指针数组下标JAVA_Java语言中可用下标和指针两种方式表示数组元素。

    [判断题]一行可以写多条预处理命令 [判断题]如果一个java程序中有多个类,编译后只生成一个字节码文件,其名字同主类名一致. [判断题]Protected类型的实例变量只能在本类中使用,其他类中不可 ...

最新文章

  1. dedecms /member/reg_new.php SQL Injection Vul
  2. 人脸对齐--Face Alignment by Explicit Shape Regression
  3. 什么是CMU Pronoucing Dictionary(CMU发音词典)
  4. 怎样取SAP中的业务对象状态
  5. REALM后续:最近邻搜索,MIPS,LSH和ALSH
  6. JavaScript prototype 属性
  7. 全国计算机等级考试题库二级C操作题100套(第95套)
  8. PHP程序中时间戳,php 时间戳常用代码
  9. python中zip的使用_浅谈Python中的zip()与*zip()函数详解
  10. jquery-事件委托-delegate
  11. UIView你知道多少
  12. HRCAD2008 无法加载问题
  13. 解读2022城市大脑首批三项标准
  14. python开发桌面时钟_python+PyQT实现系统桌面时钟
  15. 自动获取win10锁屏壁纸脚本
  16. 编译原理c++基于LR分析表编写语法分析器
  17. python自学视频与excel_小白也能学习的 python pandas excel 处理[视频]
  18. 数据结构(二)----线性表(List)链式存储结构(1)
  19. YSO小游戏·VB6版代码
  20. 数据结构 课程设计报告

热门文章

  1. 4. 爸爸喜欢做的事有钓鱼、看书 妈妈喜欢做的事有画画、烹饪 儿子继承了父母双方的爱好,输出儿子喜欢做的事
  2. 重大新闻:借贷宝不用绑卡了,借贷宝APP推出肖像识别新功能!
  3. 常见的linux包管理软件,5款最适合新手的包管理器
  4. 使用阿里云OSS实现文件上传
  5. 打破经典计算模拟限制,研究实现54个量子比特QAOA模拟优化
  6. 法兰克福------萨尔麦克拉赫小镇
  7. element 表格 点击单元格编辑,行合计
  8. 虚拟机NAT模式和桥接模式分析
  9. 第五十一章 管理镜像 - 镜像的数据库注意事项
  10. 16g内存 32g内存游戏区别_电脑内存8G和16G的差别大吗?打游戏需要多大内存?