Spring事务管理—(@Transactional)-官方原版

Spring事务管理—(@Transactional)-官方原版

计算机毕设项目资讯获取:

大家点赞、收藏、关注、评论啦 、查看👇🏻👇🏻👇🏻获取项目下载链接,博主联系方式👇🏻👇🏻👇🏻

链接点击直达:下载链接


全面的事务支持是使用Spring的最令人信服的理由之一。 框架。Spring 框架为事务提供了一致的抽象 具有以下优势的管理:

  • 跨不同事务 API(如 Java)的一致编程模型 事务 API (JTA)、JDBC、Hibernate 和 Java Persistence API (JPA)。

  • 支持声明式事务管理

  • 用于编程事务管理的更简单的 API 而不是复杂的事务 API,例如 JTA。

  • 与 Spring 的数据访问抽象完美集成。

以下部分描述了 Spring 框架的事务特性和 技术:

本章还包括对最佳实践、应用程序服务器集成、 以及常见问题的解决方案

1. Spring 框架事务支持模型的优势

传统上,J2EE 应用程序开发人员有两种事务管理选择: 全局或本地事务,两者都有深刻的局限性。全局 和本地事务管理将在接下来的两个部分中进行回顾,然后是 讨论 Spring 框架的事务管理支持如何解决 全局和本地事务模型的局限性。

1.1全局事务

全局事务允许您使用多个事务资源,通常 关系数据库和消息队列。应用程序服务器管理全局 通过 JTA 进行交易,这是一个繁琐的 API(部分原因是 异常模型)。此外,JTA UserTransaction通常需要来自 JNDI,这意味着您还需要使用 JNDI 才能使用 JTA。全局事务限制了应用程序代码的任何潜在重用,因为 JTA 是 通常仅在应用程序服务器环境中可用。

以前,使用全局事务的首选方法是通过 EJB CMT (容器管理事务)。CMT 是声明式交易的一种形式 管理(有别于程序化事务管理)。EJB CMT 消除了与事务相关的 JNDI 查找的需要,尽管使用了 EJB 本身需要使用 JNDI。它消除了大部分但不是全部的写入需求 用于控制事务的 Java 代码。显着的缺点是CMT与JTA绑定。 和应用程序服务器环境。此外,它仅在选择时才可用 在 EJB 中实现业务逻辑(或至少在事务性 EJB 外观后面)。这 一般来说,EJB的负面因素是如此之大,以至于这不是一个有吸引力的主张, 特别是面对声明式事务管理的令人信服的替代方案。

1.2 本地事务

本地事务是特定于资源的,例如与 JDBC 关联的事务 连接。本地事务可能更易于使用,但有一个明显的缺点: 它们不能跨多个事务资源工作。例如,管理 使用 JDBC 连接的事务不能在全局 JTA 事务中运行。因为 应用程序服务器不参与事务管理,它不能帮助确保 跨多个资源的正确性。(值得注意的是,大多数应用程序使用 单个事务资源)。另一个缺点是本地交易是侵入性的 到编程模型。

2. 理解 Spring 框架事务抽象

Spring 事务抽象的关键是事务策略的概念。一个 事务策略由TransactionManager定义,特别是org.springframework.transaction.PlatformTransactionManager接口事务管理和org.springframework.transaction.ReactiveTransactionManager事务管理。

以下清单显示了 API 的定义:

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

这主要是一个服务提供程序接口 (SPI),尽管您可以从应用程序代码以编程方式使用它。因为PlatformTransactionManager是一个接口

从Spring Framework 5.2开始,Spring还提供了事务管理抽象。

以下是rg.springframework.transaction.ReactiveTransactionManager的定义:

public interface ReactiveTransactionManager extends TransactionManager {

    Mono<ReactiveTransaction> getReactiveTransaction(TransactionDefinition definition) throws TransactionException;

    Mono<Void> commit(ReactiveTransaction status) throws TransactionException;

    Mono<Void> rollback(ReactiveTransaction status) throws TransactionException;
}

TransactionDefinition接口说明:

  • 传播:通常,事务范围内的所有代码都在 那笔交易。但是,您可以指定行为,如果 当事务上下文已存在时,将运行事务方法。为 例如,代码可以继续在现有事务中运行(常见情况),或者 可以暂停现有事务并创建新事务。春天 提供了 EJB CMT 中熟悉的所有事务传播选项。要阅读 关于 Spring 中事务传播的语义,请参阅 事务传播

  • 隔离:此事务与其他事务的工作隔离的程度 交易。例如,此事务是否可以看到来自其他事务的未提交写入 交易?

  • 超时:此事务在超时并自动回滚之前运行的时间 通过底层事务基础结构。

  • 只读状态:当代码读取但 不修改数据。在某些方面,只读事务可能是一个有用的优化 情况,例如当您使用休眠时。

TransactionStatus:

public interface TransactionStatus extends TransactionExecution, SavepointManager, Flushable {

    @Override
    boolean isNewTransaction();

    boolean hasSavepoint();

    @Override
    void setRollbackOnly();

    @Override
    boolean isRollbackOnly();

    void flush();

    @Override
    boolean isCompleted();
}

3.声明式事务管理

pring 框架的声明式事务管理可以通过 Spring 实现 面向方面的编程 (AOP)。但是,随着事务方面的代码出现 与 Spring 框架发行版一起使用,可以以样板方式使用 AOP 通常不必理解概念即可有效使用此代码。

Spring 框架的声明式事务管理类似于 EJB CMT,因为它 您可以指定事务行为(或缺少事务行为)到单个方法级别。 您可以在事务上下文中进行调用,如果 必要。两种类型的事务管理之间的区别是:setRollbackOnly()

  • 与与JTA绑定的EJB CMT不同,Spring Framework的声明式事务。 管理适用于任何环境。它可以与JTA事务或本地 使用 JDBC、JPA 或休眠通过调整配置进行事务 文件。

  • 您可以将 Spring 框架声明式事务管理应用于任何类, 不仅仅是像EJB这样的特殊类。

  • Spring 框架提供了声明式回滚规则,这是一个没有 EJB 的特性 等效。提供对回滚规则的编程和声明性支持。

  • Spring 框架允许您使用 AOP 自定义事务行为。 例如,您可以在事务回滚的情况下插入自定义行为。你 还可以添加任意建议以及事务性建议。使用 EJB CMT,您可以 不能影响容器的事务管理,但setRollbackOnly() 除外。

  • Spring 框架不支持跨事务上下文的传播 远程调用,就像高端应用程序服务器一样。如果您需要此功能,我们 建议您使用 EJB。但是,在使用此类功能之前,请仔细考虑, 因为,通常情况下,人们不希望事务跨越远程调用。

回滚规则的概念很重要。它们允许您指定哪些例外 (和可抛掷对象)应导致自动回滚。您可以在 配置,而不是在 Java 代码中。所以,虽然你仍然可以打电话 回滚当前事务的对象,最常见的是 可以指定必须始终导致回滚的规则。这 此选项的显著优点是业务对象不依赖于 事务基础结构。例如,他们通常不需要导入Spring。 事务 API 或其他 Spring API。

尽管 EJB 容器缺省行为会自动回滚 系统异常(通常是运行时异常),EJB CMT 不会回滚 在应用程序异常(即已检查的异常)上自动执行事务 除了 )。虽然 Spring 默认行为 声明式事务管理遵循 EJB 约定(回滚仅自动进行 在未经检查的异常中),自定义此行为通常很有用

3.1 Spring 框架的声明式事务实现

仅仅告诉您用@Transactional注释来注释您的类,添加@EnableTransactionManagement到您的配置中是不够的, 并期望您了解这一切是如何工作的。为了提供更深入的理解,这 部分解释了 Spring 框架声明式事务的内部工作原理 交易相关问题背景下的基础设施

关于 Spring 框架的声明式要掌握的最重要的概念 事务支持是通过 AOP 代理启用的,并且事务 建议由元数据(目前基于 XML 或注释)驱动。AOP的组合 使用事务元数据生成一个 AOP 代理,该代理使用 TransactionInterceptor 结合TransactionManager适当的实现围绕方法调用来驱动事务 。

Spring 框架提供事务管理 命令式和响应式编程模型。

下图显示了在事务代理上调用方法的概念视图:

3.2 声明式事务实现示例

public interface FooService {

    Foo getFoo(String fooName);

    Foo getFoo(String fooName, String barName);

    void insertFoo(Foo foo);

    void updateFoo(Foo foo);

}

以下示例显示了上述接口的实现:

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) {
        // ...
    }
}

假设接口FooService 的前两个方法getFoo(String) 和 getFoo(String, String) 必须在具有只读的事务上下文中运行 语义和其他方法,并且必须 在具有读写语义的事务上下文中运行。以下 配置将在接下来的几段中详细说明:

<!-- 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"/>

    <!-- the transactional advice (what 'happens'; see the <aop:advisor/> bean below) -->
    <tx:advice id="txAdvice" transaction-manager="txManager">
        <!-- 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="fooServiceOperation" expression="execution(* x.y.service.FooService.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceOperation"/>
    </aop:config>

    <!-- don't forget the DataSource -->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
        <property name="url" value="jdbc:oracle:thin:@rj-t42:1521:elvis"/>
        <property name="username" value="scott"/>
        <property name="password" value="tiger"/>
    </bean>

    <!-- similarly, don't forget the TransactionManager -->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!-- other <bean/> definitions here -->

</beans>

元素中定义的表达式是 AspectJ 切入点 aop:pointcut/表达

一个常见的要求是使整个服务层具有事务性。最好的方式 这样做是为了更改切入点表达式以匹配 服务层。以下示例演示如何执行此操作:

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

前面显示的配置用于围绕对象创建事务代理 这是从fooService Bean 定义创建的。代理配置为 事务性建议,以便在代理上调用适当的方法时, 事务已启动、挂起、标记为只读等,具体取决于 与该方法关联的事务配置。考虑以下程序 该测试驱动前面显示的配置:

public final class Boot {

    public static void main(final String[] args) throws Exception {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("context.xml");
        FooService fooService = ctx.getBean(FooService.class);
        fooService.insertFoo(new Foo());
    }
}

下面的清单显示了以前使用的修改版本FooService,但是 这次代码使用反应式类型:

public interface FooService {

    Flux<Foo> getFoo(String fooName);

    Publisher<Foo> getFoo(String fooName, String barName);

    Mono<Void> insertFoo(Foo foo);

    Mono<Void> updateFoo(Foo foo);

}

以下示例显示了上述接口的实现:

public class DefaultFooService implements FooService {

    @Override
    public Flux<Foo> getFoo(String fooName) {
        // ...
    }

    @Override
    public Publisher<Foo> getFoo(String fooName, String barName) {
        // ...
    }

    @Override
    public Mono<Void> insertFoo(Foo foo) {
        // ...
    }

    @Override
    public Mono<Void> updateFoo(Foo foo) {
        // ...
    }
}

命令式和响应式事务管理共享相同的事务语义 边界和事务属性定义。命令式的主要区别 反应易是后者的递延性质。 TransactionInterceptor使用事务运算符修饰返回的反应类型,以开始并清理事务。因此,调用事务反应式方法会延迟实际 对激活反应式处理的订阅类型的事务管理 类型。

3.3 回滚声明式事务

向 Spring 框架的事务基础结构指示的推荐方法 事务的工作要回滚是抛出一个 from 代码 当前正在事务上下文中执行。Spring 框架的 事务基础结构代码在冒泡时捕获任何未处理的内容 调用堆栈,并确定是否将事务标记为回滚。

回滚规则

回滚规则确定当给定异常 抛出,规则基于异常类型或异常模式。

回滚规则可以通过 rollback-for and o-rollback-for属性在 XML 中配置,这允许将规则定义为模式。使用@Transactional时,回滚规则可能会 通过 rollbackFor/noRollbackFor和 rollbackForClassName/noRollbackForClassName 属性进行配置,这些属性允许规则 分别基于异常类型或模式定义

以下 XML 代码段演示如何为选中的、 通过rollback-for属性提供异常模式实现特定于应用程序的异常类型:

<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="*" rollback-for="Throwable" no-rollback-for="InstrumentNotFoundException"/>
    </tx:attributes>
</tx:advice>

还可以以编程方式指示所需的回滚。虽然简单,但这个过程 非常具有侵入性,并且将您的代码与 Spring 框架的事务紧密耦合 基础设施。下面的示例演示如何以编程方式指示必需的 rollback:

public void resolvePosition() {
    try {
        // some business logic...
    } catch (NoProductInStockException ex) {
        // trigger rollback programmatically
        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    }
}

3.4 使用@Transactional

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

@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) {
        // ...
    }
}

与命令式相反,反应式事务方法使用反应式返回类型 编程安排如下表所示:

@Transactional
public class DefaultFooService implements FooService {

    @Override
    public Publisher<Foo> getFoo(String fooName) {
        // ...
    }

    @Override
    public Mono<Foo> getFoo(String fooName, String barName) {
        // ...
    }

    @Override
    public Mono<Void> insertFoo(Foo foo) {
        // ...
    }

    @Override
    public Mono<Void> updateFoo(Foo foo) {
        // ...
    }
}

在评估方法的事务设置时,最派生的位置优先。在下面的示例中,DefaultFooService类在类级别使用只读事务的设置进行注释,但同一类中updateFoo(Foo)方法上的@Transactional注释优先于在类级别定义的事务设置。

@Transactional(readOnly = true)
public class DefaultFooService implements FooService {

    public Foo getFoo(String fooName) {
        // ...
    }

    // these settings have precedence for this method
    @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
    public void updateFoo(Foo foo) {
        // ...
    }
}
@Transactional设置

注释是指定接口、类、 或方法必须具有事务语义(例如,“启动全新的只读 调用此方法时的事务,暂停任何现有事务“)。 默认设置如下:@Transactional@Transactional

  • 传播设置为PROPAGATION_REQUIRED.

  • 隔离级别为ISOLATION_DEFAULT.

  • 事务是读写的。

  • 事务超时默认为基础事务的默认超时 系统,如果不支持超时,则为无。

  • 任何或触发回滚,任何选中的 不。RuntimeExceptionErrorException

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

表 3.@Transactional设置

财产

类型

描述

价值

String

指定要使用的事务管理器的可选限定符。

transactionManager

String

的别名。value

label

用于向事务添加富有表现力的描述的标签数组。String

事务管理器可以评估标签,以将特定于实现的行为与实际事务相关联。

增殖

enum:Propagation

可选的传播设置。

isolation

enum:Isolation

可选隔离级别。仅适用于 或 的传播值。REQUIREDREQUIRES_NEW

timeout

int(以秒为单位)

可选的事务超时。仅适用于 或 的传播值。REQUIREDREQUIRES_NEW

timeoutString

String(以秒为单位)

将秒为单位指定为值的替代方法,例如,作为占位符。timeoutString

readOnly

boolean

读写事务与只读事务。仅适用于 或 的值。REQUIREDREQUIRES_NEW

rollbackFor

对象数组,必须派生自ClassThrowable.

必须导致回滚的异常类型的可选数组。

rollbackForClassName

异常名称模式数组。

必须导致回滚的异常名称模式的可选数组。

noRollbackFor

对象数组,必须派生自ClassThrowable.

不得导致回滚的异常类型的可选数组。

noRollbackForClassName

异常名称模式数组。

不得导致回滚的异常名称模式的可选数组。

多个事务管理器@Transactional

大多数Spring应用程序只需要一个事务管理器,但也可能需要在一个应用程序中使用多个独立的事务管理器。您可以使用@Transactional注释的value或transactionManager属性来可选地指定要使用的transactionManager的标识。这可以是bean名称,也可以是事务管理器bean的限定值。例如,使用限定符表示法,可以将以下Java代码与应用程序上下文中的以下事务管理器bean声明结合起来:

public class TransactionalService {

    @Transactional("order")
    public void setSomething(String name) { ... }

    @Transactional("account")
    public void doSomething() { ... }

    @Transactional("reactive-account")
    public Mono<Void> doSomethingReactive() { ... }
}

如果您发现在许多不同的方法上重复使用@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 {
}

3.5事务传播

本节介绍 Spring 中事务传播的一些语义。注意 本节不是对事务传播的正确介绍。相反,它 详细介绍了 Spring 中有关事务传播的一些语义。

在 Spring 管理的交易中,请注意物理和 逻辑事务,以及传播设置如何应用于此差异。

理解PROPAGATION_REQUIRED

PROPAGATION_REQUIRED强制实施物理事务,无论是本地的当前 范围,如果尚不存在事务或参与现有的“外部”事务 为更大的范围定义。这是常见调用堆栈安排中的良好默认值 在同一线程中(例如,委托给多个存储库方法的服务外观 其中所有底层资源都必须参与服务级别事务)。

传播设置为propagation_REQUIRED时,将为应用该设置的每个方法创建逻辑事务范围。每个这样的逻辑事务作用域可以单独确定仅回滚状态,外部事务作用域在逻辑上独立于内部事务作用域。在标准PROPAGATION_REQUIRED行为的情况下,所有这些作用域都映射到同一物理事务。因此,在内部事务范围中设置的仅回滚标记确实会影响外部事务实际提交的机会。

但是,在内部事务范围设置仅回滚标记的情况下,外部事务尚未决定回滚本身,因此回滚(由内部事务范围无声地触发)是意外的。此时会引发相应的UnexpectedRollbackException。这是预期的行为,因此事务的调用方永远不会被误导,以为提交是在实际上没有执行的情况下执行的。因此,如果内部事务(外部调用方不知道该事务)默默地将事务标记为仅回滚,则外部调用方仍然调用提交。外部调用方需要接收UnexpectedRollbackException,以明确表示已执行回滚。

理解 PROPAGATION_REQUIRES_NEW

理解PROPAGATION_NESTED

PROPAGATION_NESTED使用具有多个保存点的单个物理事务 它可以回滚到。这种部分回滚允许内部事务范围 触发其范围的回滚,外部事务能够继续 尽管某些操作已回滚,但物理事务。此设置 通常映射到 JDBC 保存点,因此它仅适用于 JDBC 资源事务

官网:Redirecting…

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值