最新最全面的Spring详解——事务管理
Posted 小新要变强
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了最新最全面的Spring详解——事务管理相关的知识,希望对你有一定的参考价值。
前言
本文为 【Spring】事务管理 相关知识,下边将对Spring框架事务支持模型的优点
,Spring框架的事务抽象的理解
(包含TransactionManager
、TransactionDefinition
、TransactionStatus
,编程式事务管理
(包含使用 TransactionManager
、使用TransactionTemplate
),声明式事务管理
(包含理解Spring框架的声明式事务
,声明式事务实现的示例
,事务回滚
,``tx:advice/ 设置
,使用 @Transactional
,@Transactional的设置
,带 @Transactional的多个事务管理器
,自定义注解组成
),事务传播
,及编程式和声明式事务管理之间进行选择
等进行详尽介绍~
📌博主主页:小新要变强 的主页
👉Java全栈学习路线可参考:【Java全栈学习路线】最全的Java学习路线及知识清单,Java自学方向指引,内含最全Java全栈学习技术清单~
👉算法刷题路线可参考:算法刷题路线总结与相关资料分享,内含最详尽的算法刷题路线指南及相关资料分享~
👉Java微服务开源项目可参考:企业级Java微服务开源项目(开源框架,用于学习、毕设、公司项目、私活等,减少开发工作,让您只关注业务!)
↩️本文上接:XXXX
目录
【Spring】事务管理
一、Spring框架事务支持模型的优点
- 全面的事务支持是使用Spring框架最令人信服的原因之一。 Spring Framework为事务管理提供了一个一致的抽象,给我们的开发带来了极大的便利。
- Spring允许应用程序开发人员在任何环境中使用【一致的编程模型】。 只需编写一次代码,它就可以从不同环境中的不同事务管理策略中获益。
- Spring框架同时提供【声明式】和【编程式】事务管理。 大多数用户更喜欢【声明式事务管理】,这也是我们在大多数情况下所推荐的。
- 使用声明式模型,开发人员通常【很少或不编写】与事务管理相关的代码,因此,不依赖于Spring
Framework事务API或任何其他事务API,也就是啥也不用写。
二、理解Spring框架的事务抽象
spring事务对事务抽象提现在一下三个类中:PlatformTransactionManager
,TransactionDefinition
,TransactionStatus
。
1️⃣TransactionManager
TransactionManage主要有一下两个子接口:
org.springframework.transaction.PlatformTransactionManager
接口用于为不同平台提供统一抽象的事务管理器,重要。org.springframework.transaction.ReactiveTransactionManager
接口用于响应式事务管理,这个不重要。
下面显示了’ PlatformTransactionManager ’ API的定义:
public interface PlatformTransactionManager extends TransactionManager
TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
void commit(TransactionStatus status) throws TransactionException;
void rollback(TransactionStatus status) throws TransactionException;
任何【PlatformTransactionManager】接口实现类的方法抛出的【TransactionException】是未检查的 (也就是说,它继承了【java.lang.RuntimeException】的类)。 这里边隐藏了一个知识点,我们后续再说。
public abstract class TransactionException extends NestedRuntimeException
public TransactionException(String msg)
super(msg);
public TransactionException(String msg, Throwable cause)
super(msg, cause);
任何一个【TransactionManager】的实现通常需要了解它们工作的环境:JDBC、mybatis、Hibernate等等。 下面的示例展示了如何定义一个本地的【PlatformTransactionManager】实现(在本例中,使用纯JDBC)。
你可以通过创建一个类似于下面这样的bean来定义JDBC ’ DataSource ':
username=root
password=root
url=jdbc:mysql://127.0.0.1:3306/ydlclass?characterEncoding=utf8&serverTimezone=Asia/Shanghai
driverName=com.mysql.cj.jdbc.Driver
<?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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:property-placeholder location="jdbc.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="$driverName"/>
<property name="username" value="$username"/>
<property name="password" value="$password"/>
<property name="url" value="$url"/>
</bean>
</beans>
DataSourceTransactionManager是PlatformTransactionManager的一个子类,他需要一个数据源进行注入:
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
注意点,在DataSourceTransactionManager源码中有这么一句话,将线程的持有者绑定到线程当中:
// Bind the connection holder to the thread.
if (txObject.isNewConnectionHolder())
TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());
private static final ThreadLocal<Map<Object, Object>> resources =
new NamedThreadLocal<>("Transactional resources");
从这里我们也能大致明白,PlatformTransactionManager的事务是和线程绑定的,事务的获取是从当前线程中获取的。
2️⃣TransactionDefinition
TransactionDefinition
接口指定了当前事务的相关配置,主要配置如下:
- Propagation: 通常情况下,事务范围内的所有代码都在该事务中运行。 但是,如果事务方法在 【已经存在事务的上下文中运行】,则可以指定事务的【传播行为】。
- Isolation: 该事务与其他事务的工作隔离的程度。 例如,这个事务可以看到其他事务未提交的写入吗? 【隔离级别】
- Timeout: 该事务在超时并被底层事务基础设施自动回滚之前运行多长时间。
- 只读状态: 当代码读取但不修改数据时,可以使用只读事务。 在某些情况下,如使用Hibernate时,只读事务可能是一种有用的优化。
public interface TransactionDefinition
/**
* Support a current transaction; create a new one if none exists.
*/
int PROPAGATION_REQUIRED = 0;
/**
* Support a current transaction; execute non-transactionally if none exists.
*/
int PROPAGATION_SUPPORTS = 1;
/**
* Support a current transaction; throw an exception if no current transaction
*/
int PROPAGATION_MANDATORY = 2;
/**
* Create a new transaction, suspending the current transaction if one exists.
*/
int PROPAGATION_REQUIRES_NEW = 3;
/**
* Do not support a current transaction; rather always execute non-transactionally.
*/
int PROPAGATION_NOT_SUPPORTED = 4;
/**
* Do not support a current transaction; throw an exception if a current transaction
*/
int PROPAGATION_NEVER = 5;
/**
* Execute within a nested transaction if a current transaction exists,
*/
int PROPAGATION_NESTED = 6;
int ISOLATION_DEFAULT = -1;
int ISOLATION_READ_UNCOMMITTED = 1;
int ISOLATION_READ_COMMITTED = 2;
int ISOLATION_REPEATABLE_READ = 4;
int ISOLATION_SERIALIZABLE = 8;
/**
* Use the default timeout of the underlying transaction system,
* or none if timeouts are not supported.
*/
int TIMEOUT_DEFAULT = -1;
这个接口有一个默认实现:
public class DefaultTransactionDefinition implements TransactionDefinition, Serializable
private int propagationBehavior = PROPAGATION_REQUIRED;
private int isolationLevel = ISOLATION_DEFAULT;
private int timeout = TIMEOUT_DEFAULT;
private boolean readOnly = false;
//....
3️⃣TransactionStatus
TransactionStatus
接口为事务代码提供了一种简单的方法来控制事务执行和查询事务状态。下面的例子显示了TransactionStatus
接口:
public interface TransactionStatus extends TransactionExecution, SavepointManager, Flushable
@Override
//返回当前事务是否是新的; 否则将参与现有事务,或者可能从一开始就不在实际事务中运行。
boolean isNewTransaction();
boolean hasSavepoint();
@Override
// 只设置事务回滚。 这指示事务管理器,事务的唯一可能结果可能是回滚,而不是抛出异常,从而触发回滚。
void setRollbackOnly();
@Override
// 返回事务是否被标记为仅回滚(由应用程序或事务基础设施)。
boolean isRollbackOnly();
void flush();
@Override
// 返回该事务是否已完成,即是否已提交或回滚。
boolean isCompleted();
三、编程式事务管理
Spring Framework提供了两种编程式事务管理的方法:
- 使用
TransactionTemplate
。 - 使用
TransactionManager
。
1️⃣使用 TransactionManager
🍀使用 PlatformTransactionManager
我们可以直接使用【org.springframework.transaction.PlatformTransactionManager】直接管理事务。 为此,通过bean引用将您使用的PlatformTransactionManager
的实现传递给您的bean。 然后,通过使用TransactionDefinition
和 TransactionStatus
对象,您可以发起事务、回滚和提交。 下面的示例显示了如何这样做:
给容器注入对应的事务管理器:
<context:property-placeholder location="jdbc.properties"/>
<context:component-scan base-package="com.ydlclass"/>
<!-- 注入事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 注入事务管理器 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--数据源-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="url" value="$url"/>
<property name="driverClassName" value="$driverName"/>
<property name="username" value="$user"/>
<property name="password" value="$password"/>
</bean>
注入对应的service:
@Override
public void transfer(String from, String to, Integer money)
// 默认的事务配置
DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
// 使用事务管理器进行事务管理
TransactionStatus transaction = transactionManager.getTransaction(definition);
try
// 转账其实是两个语句
String moneyFrom = "update account set money = money - ? where username = ? ";
String moneyTo = "update account set money = money + ? where username = ? ";
// 从转账的人处扣钱
jdbcTemplate.update(moneyFrom,money,from);
int i = 1/0;
jdbcTemplate.update(moneyTo,money,to);
catch (RuntimeException exception)
exception.printStackTrace();
// 回滚
transactionManager.rollback(transaction);
// 提交
transactionManager.commit(transaction);
2️⃣使用TransactionTemplate
【TransactionTemplate】采用了与其他Spring模板相同的方法,比如【JdbcTemplate】。 它使用回调方法将应用程序代码从获取和释放事务性资源的样板程序中解放出来,因为您的代码只关注您想要做的事情,而不是希望将大量的时间浪费在这里。
正如下面的示例所示,使用【TransactionTemplate】绝对会将您与Spring的事务基础设施和api耦合在一起。 编程事务管理是否适合您的开发需求,这是您必须自己做出的决定。
必须在事务上下文中运行并显式使用TransactionTemplate
的应用程序代码类似于下一个示例。 您作为一个应用程序开发人员,可以编写一个TransactionCallback
实现(通常表示为一个匿名内部类),其中包含您需要在事务上下文中运行的代码。 然后你可以将你的自定义 TransactionCallback
的一个实例传递给TransactionTemplate
中暴露的 execute(..)
方法。 下面的示例显示了如何这样做:
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="transactionManager"/>
</bean>
如果没有返回值,你可以在匿名类中使用方便的TransactionCallbackWithoutResult
类,如下所示:
@Override
public void transfer3(String from, String to, Integer money)
transactionTemplate.execute(new TransactionCallbackWithoutResult()
@Override
protected void doInTransactionWithoutResult(TransactionStatus status)
// 转账其实是两个语句
String moneyFrom = "update account set money = money - ? where username = ? ";
String moneyTo = "update account set money = money + ? where username = ? ";
// 从转账的人处扣钱
jdbcTemplate.update(moneyFrom, money, from);
// int i = 1 / 0;
jdbcTemplate.update(moneyTo, money, to);
);
四、声明式事务管理
大多数Spring框架用户选择声明式事务管理。 该选项对应用程序代码的影响最小,因此最符合非侵入式轻量级容器的理想。
Spring框架的声明性事务管理是通过Spring面向切面编程(AOP)实现的。 然而,由于事务切面代码随Spring Framework发行版一起提供,并且可以模板的方式使用,所以通常不需要理解AOP概念就可以有效地使用这些代码。
1️⃣理解Spring框架的声明式事务
Spring框架的声明式事务通过AOP代理进行实现,事务的通知是由AOP元数据与事务性元数据的结合产生了一个AOP代理,该代理使用【TransactionInterceptor】结合适当的【TransactionManager】实现来驱动方法调用的事务。
Spring Framework的【TransactionInterceptor】为命令式和响应式编程模型提供了事务管理。 拦截器通过检查方法返回类型来检测所需的事务管理风格。 事务管理风格会影响需要哪个事务管理器。 命令式事务需要【PlatformTransactionManager】,而响应式事务使用【ReactiveTransactionManager 】实现。
【@Transactional 】通常用于【PlatformTransactionManager 】管理的【线程绑定事务】,将事务暴露给当前执行线程中的所有数据访问操作。(注意:这不会传播到方法中新启动的线程)。
2️⃣声明式事务实现的示例
<!-- from the file 'context.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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- the transactional advice (what 'happens'; see the <aop:advisor/> bean below) -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!-- the transactional semantics... -->
<tx:attributes>
<!-- all methods starting with 'get' are read-only -->
<tx:method name="get*" read-only="true"/>
<!-- other methods use the default transaction settings (see below) -->
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<!-- ensure that the above transactional advice runs for any execution
of an operation defined by the FooService interface -->
<aop:config>
<aop:pointcut id="point" expression="within(com.ydlclass.service..*)"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="point"/>
</aop:config>
</beans>
3️⃣事务回滚
上一节概述了如何在应用程序中以声明的方式为类(通常是服务层类)指定事务设置的基础知识。 本节描述如何以简单的声明式方式控制事务的回滚。
重点:
在其默认配置中,Spring框架的事务基础结构代码只在运行时、未检查的异常情况下标记事务进行回滚。 也就是说,当抛出的异常是’ RuntimeException ‘的实例或子类时。 (默认情况下,’ Error '实例也会导致回滚)。 事务方法抛出的已检查异常不会导致默认配置的回滚。
您还可以准确地配置哪些“Exception”类型将事务标记为回滚。 下面的XML代码片段演示了如何为一个已检查的、特定于应用程序的“Exception”类型配置回滚:
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="get*" read-only="true" rollback-for="NoProductInStockException"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
如果您不想在抛出异常时回滚事务,您还可以指定“无回滚规则”。 下面的例子告诉Spring框架的事务基础架构,即使面对InstrumentNotFoundException`,也要提交相应的事务:
<tx:advice id="txAdvice">
<tx:attributes>
<tx:method name="updateStock" no-rollback-for="InstrumentNotFoundException"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
当Spring Framework的事务,捕获异常并参考配置的回滚规则来决定是否将事务标记为回滚时,最强匹配规则胜出。 因此,在以下配置的情况下,除了InstrumentNotFoundException之外的任何异常都会导致事务的回滚:
<tx:advice id="txAdvice">
<tx:attributes>
<tx:method name="*" rollback-for="Throwable" no-rollback-for="InstrumentNotFoundException"/>
</tx:attributes>
</tx:advice>
4️⃣<tx:advice/>
设置
本节总结了通过使
以上是关于最新最全面的Spring详解——事务管理的主要内容,如果未能解决你的问题,请参考以下文章