最新最全面的Spring详解(五)——事务管理(下)

简介: 最新最全面的Spring详解(五)——事务管理(下)

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/> 设置


本节总结了通过使用<tx:advice/> 标记可以指定的各种事务设置。 默认的<tx:advice/> 设置是:


传播行为是REQUIRED。

隔离级别为 DEFAULT。

事务处于可读写状态。

事务超时默认为底层事务系统的默认超时,如果不支持超时,则为none。

任何RuntimeException触发回滚,而任何选中的Exception不会。


您可以更改这些默认设置。 下表总结了嵌套在<tx:advice/>和<tx:attributes/>标签中的<tx:method/>标签的各种属性:

属性 Required? 默认值 描述
name Yes 要与事务属性相关联的方法名。 通配符()字符可用于将相同的事务属性设置与许多方法相关联(例如,’ get ‘、’ handle* ‘、’ on*Event '等等)。
propagation No REQUIRED 事务传播行为。
isolation No DEFAULT 事务隔离级别。 仅适用于’ REQUIRED ‘或’ REQUIRES_NEW '的传播设置。
timeout No -1 事务超时(秒)。 仅适用于传播’ REQUIRED ‘或’ REQUIRES_NEW '。
read-only No false 读写事务与只读事务。 只适用于’ REQUIRED ‘或’ REQUIRES_NEW '。
rollback-for No 触发回滚的“Exception”实例的逗号分隔列表。 例如,“com.foo.MyBusinessException, ServletException”。
no-rollback-for No 不触发回滚的“Exception”实例的逗号分隔列表。 例如,“com.foo.MyBusinessException, ServletException”。


5️⃣使用 @Transactional


除了事务配置的基于xml的声明性方法外,还可以使用基于注解的方法。 直接在Java源代码中声明事务语义使声明更接近受影响的代码。 不存在过多耦合的危险,因为要以事务方式使用的代码几乎总是以这种方式部署的。


使用’ @Transactional '注解所提供的易用性可以用一个示例进行最好的说明,下面的文本将对此进行解释。 考虑以下类定义:

// the service class that we want to make transactional
@Transactional
public class DefaultFooService implements FooService {
    @Override
    public Foo getFoo(String fooName) {
        // ...
    }
    @Override
    public Foo getFoo(String fooName, String barName) {
        // ...
    }
    @Override
    public void insertFoo(Foo foo) {
        // ...
    }
    @Override
    public void updateFoo(Foo foo) {
        // ...
    }
}


在如上所述的类级别上使用,注解指示声明类(及其子类)的所有方法的默认值。 或者,每个方法都可以单独注解。 请注意,类级注解并不适用于类层次结构中的祖先类; 在这种情况下,继承的方法需要在本地重新声明,以便参与子类级别的注解。


当上述POJO类被定义为Spring上下文中的bean时,您可以通过“@Configuration”类中的“@EnableTransactionManagement”注解使bean实例具有事务性。


在XML配置中,<tx:annotation-driven transaction-manager="txManager"/>标签提供了类似的便利:

<!-- 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">
    <!-- this is the service object that we want to make transactional -->
    <bean id="fooService" class="x.y.service.DefaultFooService"/>
    <!-- enable the configuration of transactional behavior based on annotations -->
    <!-- a TransactionManager is still required -->
    <tx:annotation-driven transaction-manager="txManager"/> 
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!-- (this dependency is defined somewhere else) -->
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <!-- other <bean/> definitions here -->
</beans>

如果要连接的’ TransactionManager ‘的bean名称为’ TransactionManager ‘,则可以省略’ tx:annotation-driven/ ‘标记中的’ transaction-manager ‘属性。 如果您想要依赖注入的’ TransactionManager ’ bean有任何其他名称,您必须使用’ transaction-manager '属性,如前面的示例所示。


注意:


(1)当你在Spring的标准配置中使用事务性代理时,你应该【只把@Transactional注解应用到public 的方法上】。如果使用’ @Transactional ‘注解’ protected ‘、’ private’或包可见的方法,则不会引发错误,但已注解的方法中事务不会生效。

(2)Spring团队建议只使用【 @Transactional】注解来注解具体的类(以及具体类的方法),而不是注解接口。当然,您可以将’ @Transactional '注解放在接口(或接口方法)上,但只有当您使用基于接口的代理时,它才会发挥作用。Java注解的事实并不意味着继承接口,如果使用基于类的代理(proxy-target-class = " true ")或weaving-based方面('模式=“aspectj”),事务设置不认可的代理和编织的基础设施,和对象不是包在一个事务代理。

(3)在代理模式(这是默认的)中,只有通过代理进入的外部方法调用会被拦截。这意味着,即使被调用的方法被标记为【@Transactional】,自调用(实际上是目标对象中的一个方法调用目标对象的另一个方法)在运行时也不会产生事务。


6️⃣@Transactional的设置


【@Transactional】注解是元数据,它指定接口、类或方法必须具有事务性语义(例如,“在调用此方法时启动一个全新的只读事务,暂停任何现有事务”)。 默认的【@Transactiona】设置如下:


传播设置为 PROPAGATION_REQUIRED。

隔离级别为 ISOLATION_DEFAULT。

事务处于可读写状态。

事务超时默认为底层事务系统的默认超时,如果不支持超时,则为none。

任何RuntimeException触发回滚,而任何选中的Exception不会。

您可以更改这些默认设置。 下表总结了@Transactional注解的各种属性:


特质 类型 描述
value String 指定要使用的事务管理器的可选限定符。
propagation enum: Propagation 可选的传播环境。
isolation enum: Isolation 可选的隔离级别。 仅适用于REQUIRED 或 REQUIRES_NEW的传播值。
timeout int(以秒为粒度) 可选的事务超时。 仅适用于 REQUIRED 或 REQUIRES_NEW的传播值。
readOnly boolean 读写事务与只读事务。 只适用于 REQUIRED 或 REQUIRES_NEW的值。
rollbackFor Class 对象的数组,它必须派生自Throwable. 必须导致回滚的异常类的可选数组。
rollbackForClassName 类名数组。 类必须派生自Throwable. 必须导致回滚的异常类名称的可选数组。
noRollbackFor Class 对象的数组,它必须派生自Throwable. 不能导致回滚的异常类的可选数组。
noRollbackForClassName String类名数组,它必须派生自 Throwable. 异常类名称的可选数组,该数组必须不会导致回滚。
label 数组String标签,用于向事务添加富有表现力的描述。 事务管理器可以评估标签,以将特定于实现的行为与实际事务关联起来。


目前,您不能显式地控制事务的名称,其中“name”指出现在事务监视器(例如,WebLogic的事务监视器)和日志输出中的【事务名称】。 对于声明性事务,事务名总是完全限定类名+ ‘.’ +事务通知类的方法名。 例如,如果’ BusinessService ‘类的’ handlePayment(…) '方法启动了一个事务,事务的名称将是: com.example.BusinessService.handlePayment。


7️⃣带 @Transactional的多个事务管理器


大多数Spring应用程序只需要一个事务管理器,但是在某些情况下,您可能希望在一个应用程序中有多个独立的事务管理器。 您可以使用’ @Transactional '注解的【value】或【transactionManager】属性来指定要使用的【transactionManage】的标识。 这可以是bean名,也可以是事务管理器bean的限定符值。 例如,使用限定符表示法,您可以在应用程序上下文中将下列Java代码与下列事务管理器bean声明组合起来:


下面的例子定义了三个事务管理器:

<tx:annotation-driven/>
<bean id="transactionManager1" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    ...
    <qualifier value="order"/>
</bean>
<bean id="transactionManager2" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    ...
    <qualifier value="account"/>
</bean>
<bean id="transactionManager3" class="org.springframework.data.r2dbc.connectionfactory.R2dbcTransactionManager">
    ...
    <qualifier value="reactive-account"/>
</bean>

下面的例子中,不同的事务使用了不同的事务管理器:

public class TransactionalService {
    @Transactional("order")
    public void setSomething(String name) { ... }
    @Transactional("account")
    public void doSomething() { ... }
    @Transactional("reactive-account")
    public Mono<Void> doSomethingReactive() { ... }
}

在这种情况下,【TransactionalService】上的各个方法在单独的事务管理器下运行,通过’ order ‘、’ account ‘和’ reactive-account ‘限定符进行区分。 如果没有找到特别限定的’ transactionManager ’ bean,则仍然使用默认的<tx:annotation-driven>中定义的bean。


8️⃣自定义注解组成


如果您发现在许多不同的方法上重复使用带有【@Transactional】的相同属性,【Spring的元注解支持】允许您为特定的用例定义自定义的组合注解。 例如,考虑以下注解定义:

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Transactional(transactionManager = "order", label = "causal-consistency")
public @interface OrderTx {
}
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Transactional(transactionManager = "account", label = "retryable")
public @interface AccountTx {
}

前面的注解让我们将上一节中的示例编写为如下所示:

public class TransactionalService {
    @OrderTx
    public void setSomething(String name) {
        // ...
    }
    @AccountTx
    public void doSomething() {
        // ...
    }
}

在前面的示例中,我们使用语法来定义事务管理器限定符和事务标签,但是我们还可以包括传播行为、回滚规则、超时和其他特性。


五、事务传播


事务的传播通常发生在【一个service层中的方法】调用【其他service层中的方法】,虽然我们并不推荐这么做,但是的确会存在一个【大的业务】包含多个【小业务】,大业务和小业务都可独立运行的场景。


比如【销售】中可以包含【增加积分】的操作,而【加积分也可以独立运行】(比如某天搞活动,给老用户直接冲积分),其中销售的方法会有事务,加积分的方法也会有事务,当销售的方法调用加积分的方法时,加积分的【小事务】就被传播到了销售这个【大事务】当中。


当事务发生传播时,一般有以下几种解决方案:


传播行为 含义
PROPAGATION_REQUIRED 表示当前方法必须运行在事务中。如果当前事务存在,方法将会在该事务中运行。否则,会启动一个新的事务
PROPAGATION_SUPPORTS 表示当前方法不需要事务上下文,但是如果存在当前事务的话,那么该方法会在这个事务中运行
PROPAGATION_MANDATORY 表示该方法必须在事务中运行,如果当前事务不存在,则会抛出一个异常
PROPAGATION_REQUIRED_NEW 表示当前方法必须运行在它自己的事务中。一个新的事务将被启动。如果存在当前事务,在该方法执行期间,当前事务会被挂起。如果使用JTATransactionManager的话,则需要访问TransactionManager
PROPAGATION_NOT_SUPPORTED 表示该方法不应该运行在事务中。如果存在当前事务,在该方法运行期间,当前事务将被挂起。如果使用JTATransactionManager的话,则需要访问TransactionManager
PROPAGATION_NEVER 表示当前方法不应该运行在事务上下文中。如果当前正有一个事务在运行,则会抛出异常
PROPAGATION_NESTED 表示如果当前已经存在一个事务,那么该方法将会在嵌套事务中运行。嵌套的事务可以独立于当前事务进行单独地提交或回滚。如果当前事务不存在,


隔离级别 含义
ISOLATION_DEFAULT 使用后端数据库默认的隔离级别
ISOLATION_READ_UNCOMMITTED 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读
ISOLATION_READ_COMMITTED 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生
ISOLATION_REPEATABLE_READ 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生
ISOLATION_SERIALIZABLE 最高的隔离级别,完全服从ACID的隔离级别,确保阻止脏读、不可重复读以及幻读,也是最慢的事务隔离级别,因为它通常是通过完全锁定事务相关的数据库表来实现的


六、在编程式和声明式事务管理之间进行选择


只有在有少量事务操作的情况下,编程式事务管理通常是一个好主意。 例如,如果您有一个web应用程序,它只需要为某些更新操作使用事务,那么您可能不希望使用Spring或任何其他技术来设置事务代理。 在这种情况下,使用TransactionTemplate可能是一种很好的方法。 只有使用事务管理的编程方法才能显式地设置事务名称。


另一方面,如果应用程序有许多事务操作,则声明式事务管理通常是值得的。 它使事务管理远离业务逻辑,并且不难配置。 当使用Spring框架而不是EJB CMT时,声明性事务管理的配置成本大大降低了。

相关文章
|
7月前
|
Java 数据库 Spring
【spring(四)】Spring事务管理和@Transactional注解
【spring(四)】Spring事务管理和@Transactional注解
102 0
|
20天前
|
XML Java 数据库连接
Spring高手之路25——深入解析事务管理的切面本质
本篇文章将带你深入解析Spring事务管理的切面本质,通过AOP手动实现 @Transactional 基本功能,并探讨PlatformTransactionManager的设计和事务拦截器TransactionInterceptor的工作原理,结合时序图详细展示事务管理流程,最后引导分析 @Transactional 的代理机制源码,帮助你全面掌握Spring事务管理。
27 2
Spring高手之路25——深入解析事务管理的切面本质
|
2月前
|
Java Spring 容器
Spring IOC、AOP与事务管理底层原理及源码解析
【10月更文挑战第1天】Spring框架以其强大的控制反转(IOC)和面向切面编程(AOP)功能,成为Java企业级开发中的首选框架。本文将深入探讨Spring IOC和AOP的底层原理,并通过源码解析来揭示其实现机制。同时,我们还将探讨Spring事务管理的核心原理,并给出相应的源码示例。
140 9
|
7月前
|
Java 关系型数据库 数据库
Spring Boot多数据源及事务管理:概念与实战
【4月更文挑战第29天】在复杂的企业级应用中,经常需要访问和管理多个数据源。Spring Boot通过灵活的配置和强大的框架支持,可以轻松实现多数据源的整合及事务管理。本篇博客将探讨如何在Spring Boot中配置多数据源,并详细介绍事务管理的策略和实践。
514 3
|
3月前
|
Java 数据库连接 数据库
Spring基础3——AOP,事务管理
AOP简介、入门案例、工作流程、切入点表达式、环绕通知、通知获取参数或返回值或异常、事务管理
Spring基础3——AOP,事务管理
|
4月前
|
XML Java 数据库
Spring5入门到实战------15、事务操作---概念--场景---声明式事务管理---事务参数--注解方式---xml方式
这篇文章是Spring5框架的实战教程,详细介绍了事务的概念、ACID特性、事务操作的场景,并通过实际的银行转账示例,演示了Spring框架中声明式事务管理的实现,包括使用注解和XML配置两种方式,以及如何配置事务参数来控制事务的行为。
Spring5入门到实战------15、事务操作---概念--场景---声明式事务管理---事务参数--注解方式---xml方式
|
4月前
|
Java Spring 开发者
掌握Spring事务管理,打造无缝数据交互——实用技巧大公开!
【8月更文挑战第31天】在企业应用开发中,确保数据一致性和完整性至关重要。Spring框架提供了强大的事务管理机制,包括`@Transactional`注解和编程式事务管理,简化了事务处理。本文深入探讨Spring事务管理的基础知识与高级技巧,涵盖隔离级别、传播行为、超时时间等设置,并介绍如何使用`TransactionTemplate`和`PlatformTransactionManager`进行编程式事务管理。通过合理设计事务范围和选择合适的隔离级别,可以显著提高应用的稳定性和性能。掌握这些技巧,有助于开发者更好地应对复杂业务需求,提升应用质量和可靠性。
49 0
|
5月前
|
Java 数据库连接 API
Spring事务管理嵌套事务详解 : 同一个类中,一个方法调用另外一个有事务的方法
Spring事务管理嵌套事务详解 : 同一个类中,一个方法调用另外一个有事务的方法
351 1
|
6月前
|
Java 开发者 Spring
深入解析 @Transactional:Spring 事务管理的艺术及实战应对策略
深入解析 @Transactional:Spring 事务管理的艺术及实战应对策略
|
5月前
|
Java Spring
解析Spring Boot中的事务管理机制
解析Spring Boot中的事务管理机制