Spring事务核心对象

J2EE开发使用分层设计的思想进行,对于简单的业务层转调数据层的单一操作,事务开启在业务层或者数据层并无太大差别,当业务中包含多个数据层的调用时,需要在业务层开启事务,对数据层中多个操作进行组合并归属于同一个事务进行处理

Spring为业务层提供了整套的事务解决方案:

  • PlatformTransactionManager
  • TransactionDefinition
  • TransactionStatus

PlatformTransactionManager

PlatformTransactionManager:平台事务管理器实现类,是一个接口,需要使用它的实现类

  • DataSourceTransactionManager 适用于Spring JDBC或MyBatis

  • HibernateTransactionManager 适用于Hibernate3.0及以上版本

  • JpaTransactionManager 适用于JPA

  • JdoTransactionManager 适用于JDO

  • JtaTransactionManager 适用于JTA

  • JPA(Java Persistence API)Java EE 标准之一,为POJO提供持久化标准规范,并规范了持久化开发的统一API,符合JPA规范的开发可以在不同的JPA框架下运行

  • JDO(Java Data Object )是Java对象持久化规范,用于存取某种数据库中的对象,并提供标准化API。与JDBC相比,JDBC仅针对关系数据库进行操作,JDO可以扩展到关系数据库、文件、XML、对象数据库(ODBMS)等,可移植性更强

  • JTA(Java Transaction API)Java EE 标准之一,允许应用程序执行分布式事务处理。与JDBC相比,JDBC事务则被限定在一个单一的数据库连接,而一个JTA事务可以有多个参与者,比如JDBC连接、JDO 都可以参与到一个JTA事务中

此接口定义了事务的基本操作

  • 获取事务 :
TransactionStatus getTransaction(TransactionDefinition definition)
  • 提交事务 :
void commit(TransactionStatus status)
  • 回滚事务 :
void rollback(TransactionStatus status)

TransactionDefinition

此接口定义了事务的基本信息

  • 获取事务定义名称
String getName()
  • 获取事务的读写属性
boolean isReadOnly()
  • 获取事务隔离级别
int getIsolationLevel()
  • 获事务超时时间
int getTimeout()
  • 获取事务传播行为特征
int getPropagationBehavior()

TransactionStatus

此接口定义了事务在执行过程中某个时间点上的状态信息及对应的状态操作

获取事务是否处于新开启事务状态

boolean isNewTransaction()

获取事务是否处于已完成状态

boolean isCompleted()

获取事务是否处于回滚状态

boolean isRollbackOnly()

刷新事务状态

void flush()

获取事务是否具有回滚存储点

boolean hasSavepoint()

设置事务处于回滚状态

void setRollbackOnly()

事务控制方式

  • 编程式

  • 声明式(XML)

  • 声明式(注解)

案例:

模拟银行转账业务说明,银行转账操作中,涉及从A账户到B账户的资金转移操作。数据层仅提供单条数据的基础操作,未设计多账户间的业务操作。

案例环境(基于Spring、Mybatis整合):

pom.xml

   <dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.1.9.RELEASE</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>5.1.9.RELEASE</version></dependency><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.3</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.47</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.16</version></dependency><dependency><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId><version>1.3.0</version></dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.4</version></dependency><dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter</artifactId><version>RELEASE</version><scope>test</scope></dependency></dependencies>

applicationContext.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:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aophttps://www.springframework.org/schema/aop/spring-aop.xsd"><context:property-placeholder location="classpath:*.properties"/><bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="driverClassName" value="${jdbc.driver}"/><property name="url" value="${jdbc.url}"/><property name="username" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/></bean><bean id="accountService" class="com.itzhuzhu.service.impl.AccountServiceImpl"><property name="accountDao" ref="accountDao"/><property name="dataSource" ref="dataSource"/></bean><bean class="org.mybatis.spring.SqlSessionFactoryBean"><property name="dataSource" ref="dataSource"/><property name="typeAliasesPackage" value="com.itzhuzhu.domain"/></bean><bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"><property name="basePackage" value="com.itzhuzhu.dao"/></bean></beans>

jdbc.properties

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/db1
jdbc.username=root
jdbc.password=不告诉你

AccountDao.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itzhuzhu.dao.AccountDao"><update id="inMoney">update account set money = money + #{money} where name = #{name}</update><update id="outMoney">update account set money = money - #{money} where name = #{name}</update></mapper>

dao包下AccountDao

public interface AccountDao {void inMoney(@Param("name") String name, @Param("money") Double money);void outMoney(@Param("name") String name, @Param("money") Double money);
}

domain包下Account

public class Account implements Serializable {private Integer id;private String name;private Double money;
}

service包下AccountService

public interface AccountService {/*** 转账操作** @param outName 出账用户名* @param inName  入账用户名* @param money   转账金额*/public void transfer(String outName, String inName, Double money);
}

service.impl包下AccountServiceImpl

public class AccountServiceImpl implements AccountService {private AccountDao accountDao;private DataSource dataSource;public void setDataSource(DataSource dataSource) {this.dataSource = dataSource;}public void setAccountDao(AccountDao accountDao) {this.accountDao = accountDao;}public void transfer(String outName, String inName, Double money) {// 开启事务PlatformTransactionManager pt = new DataSourceTransactionManager(dataSource);// 事务定义TransactionDefinition td = new DefaultTransactionDefinition();// 事务状态TransactionStatus ts = pt.getTransaction(td);accountDao.inMoney(outName, money);int i = 1 / 0;accountDao.outMoney(inName, money);// 提交事务pt.commit(ts);}
}

测试类:

public class Test {public static void main(String[] args) {ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");AccountService accountService = (AccountService) ctx.getBean("accountService");accountService.transfer("张三","李四",100D);}
}
编程式事务:
public void transfer(String outName,String inName,Double money){//创建事务管理器DataSourceTransactionManager dstm = new DataSourceTransactionManager();//为事务管理器设置与数据层相同的数据源dstm.setDataSource(dataSource);//创建事务定义对象TransactionDefinition td = new DefaultTransactionDefinition();//创建事务状态对象,用于控制事务执行TransactionStatus ts = dstm.getTransaction(td);accountDao.inMoney(outName,money);int i = 1/0;    //模拟业务层事务过程中出现错误accountDao.outMoney(inName,money);//提交事务dstm.commit(ts);
}
使用AOP控制事务

将业务层的事务处理功能抽取出来制作成AOP通知,利用环绕通知运行期动态织入。
aop包下TxAdvice

public class TxAdvice {private DataSource dataSource;public void setDataSource(DataSource dataSource) {this.dataSource = dataSource;}public Object transactionManager(ProceedingJoinPoint pjp) throws Throwable {// 开启事务PlatformTransactionManager pt = new DataSourceTransactionManager(dataSource);// 事务定义TransactionDefinition td = new DefaultTransactionDefinition();// 事务状态TransactionStatus ts = pt.getTransaction(td);Object ret = pjp.proceed(pjp.getArgs());// 提交事务pt.commit(ts);return ret;}
}

AccountServiceImpl

public class AccountServiceImpl implements AccountService {private AccountDao accountDao;private DataSource dataSource;public void setDataSource(DataSource dataSource) {this.dataSource = dataSource;}public void setAccountDao(AccountDao accountDao) {this.accountDao = accountDao;}public void transfer(String outName, String inName, Double money) {accountDao.inMoney(outName, money);int i = 1 / 0;accountDao.outMoney(inName, money);}
}

applicationContext.xml

    <!--AOP配置--><bean id="txAdvice" class="com.itzhuzhu.aop.TxAdvice"><property name="dataSource" ref="dataSource"/></bean><aop:config><aop:pointcut id="pt" expression="execution(* *..transfer(..))"/><aop:aspect ref="txAdvice"><aop:around method="transactionManager" pointcut-ref="pt"/></aop:aspect></aop:config>

声明式事务(XML)

声明式事务是由Spring操控事务,基于上面的案例改造

applicationContext.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: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/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/txhttps://www.springframework.org/schema/tx/spring-tx.xsdhttp://www.springframework.org/schema/aophttps://www.springframework.org/schema/aop/spring-aop.xsd"><context:property-placeholder location="classpath:*.properties"/><bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="driverClassName" value="${jdbc.driver}"/><property name="url" value="${jdbc.url}"/><property name="username" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/></bean><bean id="accountService" class="com.itzhuzhu.service.impl.AccountServiceImpl"><property name="accountDao" ref="accountDao"/><!--        <property name="dataSource" ref="dataSource"/>--></bean><bean class="org.mybatis.spring.SqlSessionFactoryBean"><property name="dataSource" ref="dataSource"/><property name="typeAliasesPackage" value="com.itzhuzhu.domain"/></bean><bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"><property name="basePackage" value="com.itzhuzhu.dao"/></bean><!--    &lt;!&ndash;AOP配置&ndash;&gt;<bean id="txAdvice" class="com.itzhuzhu.aop.TxAdvice"><property name="dataSource" ref="dataSource"/></bean><aop:config><aop:pointcut id="pt" expression="execution(* *..transfer(..))"/><aop:aspect ref="txAdvice"><aop:around method="transactionManager" pointcut-ref="pt"/></aop:aspect></aop:config>--><!--TX方式--><bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource"/></bean><!--定义事务管理的通知类--><tx:advice id="txAdvice" transaction-manager="txManager"><!--定义控制的事务--><tx:attributes><tx:method name="*" read-only="false"/><tx:method name="get*" read-only="true"/><tx:method name="find*" read-only="true"/><tx:methodname="transfer"read-only="false"timeout="-1"isolation="DEFAULT"no-rollback-for=""rollback-for=""propagation="REQUIRED"/><!--<tx:method name="transfer" read-only="false"/>--></tx:attributes></tx:advice><aop:config><aop:pointcut id="pt" expression="execution(* com.itzhuzhu.service.*Service.*(..))"/><aop:pointcut id="pt2" expression="execution(* com.itzhuzhu.dao.*.b(..))"/><aop:advisor advice-ref="txAdvice" pointcut-ref="pt"/><aop:advisor advice-ref="txAdvice" pointcut-ref="pt2"/></aop:config>
</beans>

使用tx命名空间配置事务专属通知类

<tx:advice id="txAdvice" transaction-manager="txManager"><tx:attributes><tx:method name="*" read-only="false" /><tx:method name="get*" read-only="true" /><tx:method name="find*" read-only="true" /></tx:attributes>
</tx:advice>

使用aop:advisor在AOP配置中引用事务专属通知类

<aop:config><aop:pointcut id="pt" expression="execution(* *..*(..))"/><aop:advisor advice-ref="txAdvice" pointcut-ref="pt"/>
</aop:config>

aop:advice与aop:advisor区别:

  • aop:advice配置的通知类可以是普通java对象,不实现接口,也不使用继承关系

  • aop:advisor配置的通知类必须实现通知接口

    • MethodBeforeAdvice

    • AfterReturningAdvice

    • ThrowsAdvice

tx配置——tx:advice

  • 名称:tx:advice

  • 类型:标签

  • 归属:beans标签

  • 作用:专用于声明事务通知

  • 格式:

<beans><tx:advice id="txAdvice" transaction-manager="txManager"></tx:advice>
</beans>
  • 基本属性:

    • id :用于配置aop时指定通知器的id

    • transaction-manager :指定事务管理器bean

tx配置——tx:attributes

  • 名称:tx:attributes

  • 类型:标签

  • 归属:tx:advice标签

  • 作用:定义通知属性

  • 格式:

<tx:advice id="txAdvice" transaction-manager="txManager"><tx:attributes></tx:attributes>
</tx:advice>
  • 基本属性:

tx配置——tx:method

  • 名称:tx:method

  • 类型:标签

  • 归属:tx:attribute标签

  • 作用:设置具体的事务属性

  • 格式:

<tx:attributes><tx:method name="*" read-only="false" /><tx:method name="get*" read-only="true" />
</tx:attributes>
  • 说明:

    通常事务属性会配置多个,包含1个读写的全事务属性,1个只读的查询类事务属性

tx:method属性:

<tx:methodname="transfer"          待添加事务的方法名表达式(支持*号通配符),例如get*、*、......read-only="false"      待设置事务的读写属性,true为只读,false为读写timeout="-1"            设置事务超时时长,单位秒isolation="DEFAULT"       设置事务隔离级别,该隔离级设定是基于spring的设定,非数据库端no-rollback-for=""        设置事务中不回滚的异常,多个异常间使用逗号分割rollback-for=""            设置事务中必回滚的异常,多个异常间使用逗号分割propagation="REQUIRED" 设置事务的传播行为

事务传播行为

  • 事务管理员

  • 事务协调员

  • 事务传播行为描述的是事务协调员对事务管理员所携带事务的处理态度

事务传播应用:

  • 场景A:生成订单业务

    • 子业务S1:记录日志到数据库表X

    • 子业务S2:保存订单数据到数据库表Y

    • 子业务S3:……

    • 如果S2或S3或……事务提交失败,此时S1是否回滚?如何控制?

    • (S1需要新事务)

  • 场景B:生成订单业务

    • 背景1:订单号生成依赖数据库中一个专门用于控制订单号编号生成的表M获取

    • 背景2:每次获取完订单号,表M中记录的编号自增1

    • 子业务S1:从表M中获取订单编号

    • 子业务S2:保存订单数据,订单编号来自于表M

    • 子业务S3:……

    • 如果S2或S3或……事务提交失败,此时S1是否回滚?如何控制?

    • (S1需要新事务)

声明式事务(注解)

@Transactional

  • 名称:@Transactional

  • 类型:方法注解,类注解,接口注解

  • 位置:方法定义上方,类定义上方,接口定义上方(不要写在实现类,实现类会换,一般写接口上,接口方法写详细的,接口类可以做一个大的控制)

  • 作用:设置当前类/接口中所有方法或具体方法开启事务,并指定相关事务属性

  • 范例:

@Transactional
public interface AccountService {/*** 转账操作** @param outName 出账用户名* @param inName  入账用户名* @param money   转账金额*/@Transactional(readOnly = false,timeout = -1,isolation = Isolation.DEFAULT,rollbackFor = {java.lang.ArithmeticException.class, IOException.class},noRollbackFor = {},propagation = Propagation.REQUIRED)public void transfer(String outName, String inName, Double money);
}

tx:annotation-driven

  • 名称:tx:annotation-driven

  • 类型:标签

  • 归属:beans标签

  • 作用:开启事务注解驱动,并指定对应的事务管理器

  • 范例:

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

声明式事务(纯注解驱动)

  • 名称:@EnableTransactionManagement

  • 类型:类注解

  • 位置:Spring注解配置类上方

  • 作用:开启注解驱动,等同XML格式中的注解驱动

  • 范例:

AccountDao

public interface AccountDao {@Update("update account set money = money + #{money} where name = #{name}")void inMoney(@Param("name") String name, @Param("money") Double money);@Update("update account set money = money - #{money} where name = #{name}")void outMoney(@Param("name") String name, @Param("money") Double money);}

AccountServiceImpl

@Service("accountService")
public class AccountServiceImpl implements AccountService {@Autowiredprivate AccountDao accountDao;public void transfer(String outName, String inName, Double money) {accountDao.inMoney(outName,money);int i = 1/0;accountDao.outMoney(inName,money);}
}

JDBCConfig

public class JDBCConfig {@Value("${jdbc.driver}")private String driver;@Value("${jdbc.url}")private String url;@Value("${jdbc.username}")private String userName;@Value("${jdbc.password}")private String password;@Bean("dataSource")public DataSource getDataSource(){DruidDataSource ds = new DruidDataSource();ds.setDriverClassName(driver);ds.setUrl(url);ds.setUsername(userName);ds.setPassword(password);return ds;}@Beanpublic PlatformTransactionManager getTransactionManager(DataSource dataSource){return new DataSourceTransactionManager(dataSource);}
}

MyBatisConfig

public class MyBatisConfig {@Beanpublic SqlSessionFactoryBean getSqlSessionFactoryBean(@Autowired DataSource dataSource){SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();ssfb.setTypeAliasesPackage("com.itzhuzhu.domain");ssfb.setDataSource(dataSource);return ssfb;}@Beanpublic MapperScannerConfigurer getMapperScannerConfigurer(){MapperScannerConfigurer msc = new MapperScannerConfigurer();msc.setBasePackage("com.itzhuzhu.dao");return msc;}
}

SpringConfig

@Configuration
@ComponentScan("com.itzhuzhu")
@PropertySource("classpath:jdbc.properties")
@Import({JDBCConfig.class,MyBatisConfig.class,TransactionManagerConfig.class})
@EnableTransactionManagement
public class SpringConfig {}
public class TransactionManagerConfig {@Beanpublic PlatformTransactionManager getTransactionManager(@Autowired DataSource dataSource){return new DataSourceTransactionManager(dataSource);}
}

Spring事务详解与使用相关推荐

  1. Spring源码(八):Spring事务详解

    Spring事务详解 一.事务执行流程 二.Spring事务切面 三.事务切面的Pointcut和Advice 四.注解事务的源码分析 五.Sping事务的传播属性 六.Sping事务的异常校验 七. ...

  2. spring事务详解

    Spring事务总结 参考 首先,如果整个系统要能实现事务,需要两个方面: Spring开启了事务 MySQL 数据库默认使用支持事务的innodb引擎 1.什么是事务? 事务是逻辑上的一组操作,要么 ...

  3. Spring高级之Spring事务详解(用法、传播行为、隔离级别、只读事务、事务条件)

    前言 这是之前开始学spring的时候的笔记,现在添加了一些理解,然后把他搬到博客上来. 事务概述: 这里仅对数据库事务进行一个概述,要详细的可以查询相关文章 在JavaEE企业级开发的应用领域,为确 ...

  4. Spring 体系结构详解

    Spring 体系结构详解 核心容器(Core Container) Core和Beans模块提供了Spring最基础的功能,提供IOC和依赖注入特性.这里的基础概念是BeanFactory,它提供对 ...

  5. struts2+hibernate+spring配置详解

    #struts2+hibernate+spring配置详解 struts2+hibernate+spring配置详解 哎 ,当初一个人做好难,现在终于弄好了,希望自学这个的能少走些弯路. 以下是自己配 ...

  6. SpringBoot事务详解

    文章目录 一.简介 1.介绍 2.事务特点 3.事务实现方式 3.1 MySql事务实现方式 3.2 SpringBoot实现机制 二.@Transactional详解 1.@Transactiona ...

  7. java JDBC事务和JTA事务详解

    什么是事务? 事务其实就是一套数据库操作集合,说到事务就不得不说它的四大特性(A C I D):原子性,一致性,隔离性,持久性.事务的原子性表示事务要么被全部执行,要么被全部不执行.如果事务下的子事务 ...

  8. Spring入门详解

    typora-copy-images-to: upload Spring入门详解 Spring框架是Java开发中最常用的框架,功能非常强大 源码下载:Spring Framework jar包.文档 ...

  9. 什么是ZooKeeper?可以做什么?ZooKeeper分布式事务详解篇

    前言 什么是ZooKeeper,你真的了解它吗.我们一起来看看吧~ 一.什么是 ZooKeeper? ZooKeeper 是 Apache 的一个顶级项目,为分布式应用提供高效.高可用的分布式协调服务 ...

最新文章

  1. K8s如何改变美团的云基础设施?
  2. 《实例化需求》读书笔记
  3. 配置 html-webpack-plugin 生成预览页面||配置自动打包相关的参数
  4. Android学习笔记(5)----启动 Theme.Dialog 主题的Activity时程序崩溃的解决办法
  5. 有关mysql的开发介绍_深入浅出MySQL之开发篇(二)
  6. 2.Hadoop HDFS 安装配置
  7. 远程计算机用户端口,电脑怎么开远程端口
  8. 软件工程中需要学习和掌握的软件都有哪些_上海理工大学869软件工程考研经验...
  9. java set collection_Java的Collection接口和Set接口?
  10. 转【es中数据节点和主机】
  11. Oracle instr用法
  12. 42Exchange 2010升级到Exchange 2013-证书申请配置
  13. 2000~2022年Java学习笔记
  14. 【华为OD机试Python实现】HJ70 矩阵乘法计算量估算(中等)
  15. 新MacBookAir (M1 2020)修改开机启登录密码
  16. JavaScript——用键盘wasd控制div移动
  17. 【Kay】MySQL必会常用函数
  18. 手把手带你撸一个校园APP(八):校园通讯录模块
  19. 介质服务器作用,爱数之介质服务器及介质同步技术
  20. torch中的inplace操作问题解决方法

热门文章

  1. python绘制如下图形、小三角形边长20_python二级操作题与分析(7)
  2. python平方数迭代器_Python三大神器之迭代器详解
  3. 如何自己去写一个鼠标驱动_为什么要用哈密顿采样器(Hamiltonian Monte Carlo),以及如何自己写一个...
  4. mysql优化 坑_mysql之我们终将踩过的坑(优化)
  5. 新版CSDN中如何快速转载别人的CSDN博客,详细方法与步骤!!!
  6. 360浏览器广告太多怎么办_360浏览器如何关闭广告自动推送
  7. java list 范围删除_JAVA中循环删除list中元素(移除list两时间范围外的元素)
  8. wacom mac驱动_MAC系统装Wacom手绘板的方法及注意事项
  9. 白盒测试-条件覆盖-短路陷阱
  10. python-week6