Spring数据访问和数据访问层与业务或服务层之间的交互

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring数据访问和数据访问层与业务或服务层之间的交互相关的知识,希望对你有一定的参考价值。

版本 6.0.0

Spring数据访问和数据访问层与业务或服务层之间的交互_hibernate

参考文档的这一部分涉及数据访问和 数据访问层与业务或服务层之间的交互。

Spring的全面事务管理支持已经详细介绍, 然后全面涵盖各种数据访问框架和技术 Spring 框架与之集成。

1. 交易管理

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

  • 跨不同事务 API(如 Java)的一致编程模型 事务 API (JTA)、JDBC、Hibernate 和 Java Persistence API (JPA)。
  • 支持声明式事务管理。
  • 用于编程事务管理的更简单的 API 而不是复杂的事务 API,例如 JTA。
  • 与 Spring 的数据访问抽象完美集成。

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

  • Spring 框架事务支持的优势 模型描述了为什么你会使用Spring Framework的事务抽象 而不是 EJB 容器管理事务 (CMT) 或选择在本地驱动 通过专有 API 进行交易,例如 Hibernate。
  • 了解 Spring 框架事务抽象概述了核心类,并描述了如何配置和获取来自各种来源的实例。DataSource
  • 将资源与事务同步说明 应用程序代码如何确保创建、重用和清理资源 适当地。
  • 声明性事务管理描述了对 声明式事务管理。
  • 程序化事务管理涵盖对以下各项的支持 编程(即显式编码)事务管理。
  • 事务绑定事件描述如何使用应用程序 事务中的事件。

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

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

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

1.1.1. 全球交易

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

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

1.1.2. 本地交易

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

1.1.3. Spring 框架的一致编程模型

Spring 解决了全球和本地交易的缺点。它让 应用程序开发人员在任何环境中都使用一致的编程模型。 您编写一次代码,它就可以从不同的事务管理中受益 不同环境中的策略。Spring 框架提供了声明式和 程序化事务管理。大多数用户更喜欢声明式事务 管理,我们在大多数情况下建议这样做。

通过编程事务管理,开发人员可以使用 Spring 框架 事务抽象,可以在任何底层事务基础结构上运行。 使用首选的声明性模型,开发人员通常编写很少或没有代码 与事务管理相关,因此不依赖于 Spring 框架 事务 API 或任何其他事务 API。

您是否需要应用程序服务器进行事务管理?

Spring 框架的事务管理支持改变了传统的规则: 当企业 Java 应用程序需要应用程序服务器时。

特别是,您不需要纯粹用于声明性事务的应用程序服务器 通过 EJB。事实上,即使您的应用服务器具有强大的 JTA 功能, 你可能会决定 Spring 框架的声明式事务提供更多的功能和 比 EJB CMT 更高效的编程模型。

通常,仅当应用程序需要时才需要应用程序服务器的 JTA 功能。 处理跨多个资源的事务,这不是许多资源的要求 应用。许多高端应用程序使用单个高度可扩展的数据库(例如 甲骨文 RAC) 取而代之。独立的事务管理器(如Atomikos Transactions和JOTM) 是其他选项。当然,您可能需要其他应用程序服务器功能,例如 Java Message Service (JMS) 和 Jakarta EE Connector Architecture (JCA)。

Spring 框架使您可以选择何时将应用程序扩展到完全 加载的应用程序服务器。使用 EJB 的唯一替代方法的日子已经一去不复返了 CMT或JTA是用本地事务(例如JDBC连接上的事务)编写代码 如果您需要该代码在容器管理的全局范围内运行,则需要大量返工 交易。使用 Spring 框架,只有 配置文件需要更改(而不是您的代码)。

1.2. 理解 Spring 框架事务抽象

Spring 事务抽象的关键是事务策略的概念。一个 事务策略由 A 定义,具体接口为命令式 事务管理和反应式接口 事务管理。以下清单显示了 API 的定义:​​TransactionManager​​​​org.springframework.transaction.PlatformTransactionManager​​​​org.springframework.transaction.ReactiveTransactionManager​​​​PlatformTransactionManager​

public interface PlatformTransactionManager extends TransactionManager 

TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;

void commit(TransactionStatus status) throws TransactionException;

void rollback(TransactionStatus status) throws TransactionException;

这主要是一个服务提供程序接口 (SPI),尽管您可以从应用程序代码以编程方式使用它。因为是一个接口,所以它可以很容易地被嘲笑或存根为 必要。它不依赖于查找策略,例如JNDI.实现的定义与任何其他对象(或bean)相同。 在 Spring Framework IoC 容器中。仅此好处就使 Spring 框架 事务是一个有价值的抽象,即使您使用 JTA 也是如此。你可以测试 事务代码比直接使用 JTA 容易得多。​​PlatformTransactionManager​​​​PlatformTransactionManager​

再次,为了与春天的哲学保持一致,可以抛出 通过任何接口的方法都是未选中的(那 是,它扩展了类)。事务基础结构 失败几乎总是致命的。在极少数情况下,应用程序代码实际上可以 从事务失败中恢复,应用程序开发人员仍然可以选择捕获 和处理。突出的一点是开发人员不是被迫这样做的。​​TransactionException​​​​PlatformTransactionManager​​​​java.lang.RuntimeException​​​​TransactionException​

该方法返回一个对象,具体取决于参数。返回的可能表示 新事务或可以表示现有事务(如果是匹配事务) 存在于当前调用堆栈中。后一种情况的含义是,与 Jakarta EE 事务上下文,与 执行。​​getTransaction(..)​​​​TransactionStatus​​​​TransactionDefinition​​​​TransactionStatus​​​​TransactionStatus​

从Spring Framework 5.2开始,Spring还提供了事务管理抽象 使用反应式类型或 Kotlin 协程的反应式应用程序。以下 列表显示由以下人员定义的交易策略:​​org.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;

反应式事务管理器主要是一个服务提供者接口 (SPI), 尽管您可以从您的 应用程序代码。因为是一个界面,所以很容易 必要时嘲笑或存根。​​ReactiveTransactionManager​

该接口指定:​​TransactionDefinition​

  • 传播:通常,事务范围内的所有代码都在 那笔交易。但是,您可以指定行为,如果 当事务上下文已存在时,将运行事务方法。为 例如,代码可以继续在现有事务中运行(常见情况),或者 可以暂停现有事务并创建新事务。春天 提供了 EJB CMT 中熟悉的所有事务传播选项。要阅读 关于 Spring 中事务传播的语义,请参阅事务传播。
  • 隔离:此事务与其他事务的工作隔离的程度 交易。例如,此事务是否可以看到来自其他事务的未提交写入 交易?
  • 超时:此事务在超时并自动回滚之前运行的时间 通过底层事务基础结构。
  • 只读状态:当代码读取但 不修改数据。在某些方面,只读事务可能是一个有用的优化 情况,例如当您使用休眠时。

这些设置反映了标准的事务概念。如有必要,请参阅资源 讨论事务隔离级别和其他核心事务概念。 理解这些概念对于使用 Spring 框架或任何 事务管理解决方案。

该接口为事务代码提供了一种简单的方法 控制事务执行和查询事务状态。这些概念应该是 熟悉,因为它们对所有事务 API 都是通用的。以下清单显示了界面:​​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();

无论您在 春天,定义正确的实现是绝对必要的。 通常通过依赖关系注入来定义此实现。​​TransactionManager​

​TransactionManager​​实现通常需要了解以下环境: 他们工作:JDBC,JTA,Hibernate等。以下示例演示如何 定义一个本地实现(在本例中,使用 plain JDBC.)​​PlatformTransactionManager​

您可以通过创建类似于以下内容的 Bean 来定义 JDBC:​​DataSource​

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="$jdbc.driverClassName" />
<property name="url" value="$jdbc.url" />
<property name="username" value="$jdbc.username" />
<property name="password" value="$jdbc.password" />
</bean>

然后,相关 bean 定义具有对定义的引用。它应类似于以下示例:​​PlatformTransactionManager​​​​DataSource​

<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>

如果您在 Jakarta EE 容器中使用 JTA,则使用容器,获得 通过JNDI,与Springs一起。以下示例 显示了 JTA 和 JNDI 查找版本的外观:​​DataSource​​​​JtaTransactionManager​

<?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:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/jee
https://www.springframework.org/schema/jee/spring-jee.xsd">

<jee:jndi-lookup id="dataSource" jndi-name="jdbc/jpetstore"/>

<bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager" />

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

</beans>

不需要知道(或任何其他 特定资源),因为它使用容器的全局事务管理 基础设施。​​JtaTransactionManager​​​​DataSource​

在所有 Spring 事务设置中,应用程序代码不需要更改。您可以更改 如何仅通过更改配置来管理事务,即使该更改意味着 从本地事务转移到全局事务,反之亦然。

1.2.1. 休眠事务设置

您还可以轻松使用 Hibernate 本地事务,如以下示例所示。 在这种情况下,您需要定义一个休眠,您的 应用程序代码可用于获取休眠实例。​​LocalSessionFactoryBean​​​​Session​

Thebean 定义类似于前面显示的本地 JDBC 示例 因此,以下示例中未显示。​​DataSource​

在这种情况下,Thebean属于该类型。在 与需要引用的方式相同,需要引用。以下 示例声明和豆子:​​txManager​​​​HibernateTransactionManager​​​​DataSourceTransactionManager​​​​DataSource​​​​HibernateTransactionManager​​​​SessionFactory​​​​sessionFactory​​​​txManager​

<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mappingResources">
<list>
<value>org/springframework/samples/petclinic/hibernate/petclinic.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<value>
hibernate.dialect=$hibernate.dialect
</value>
</property>
</bean>

<bean id="txManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>

如果使用 Hibernate 和 Jakarta EE 容器管理的 JTA 事务,则应使用 与前面的 JDBC JTA 示例中相同,如下所示 示例显示。此外,建议通过其使Hibernate知道JTA 事务协调器,可能还有其连接释放模式配置:​​JtaTransactionManager​

<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mappingResources">
<list>
<value>org/springframework/samples/petclinic/hibernate/petclinic.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<value>
hibernate.dialect=$hibernate.dialect
hibernate.transaction.coordinator_class=jta
hibernate.connection.handling_mode=DELAYED_ACQUISITION_AND_RELEASE_AFTER_STATEMENT
</value>
</property>
</bean>

<bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>

或者,您也可以将 ther传递给您的以强制执行相同的默认值:​​JtaTransactionManager​​​​LocalSessionFactoryBean​

<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mappingResources">
<list>
<value>org/springframework/samples/petclinic/hibernate/petclinic.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<value>
hibernate.dialect=$hibernate.dialect
</value>
</property>
<property name="jtaTransactionManager" ref="txManager"/>
</bean>

<bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>

1.3. 将资源与事务同步

如何创建不同的事务管理器以及如何将它们链接到相关资源 需要同步到事务(例如,到 JDBC,到休眠, 等等)现在应该很清楚了。本节介绍应用程序如何编码 (直接或间接地,通过使用持久性 API,如 JDBC、Hibernate 或 JPA) 确保正确创建、重用和清理这些资源。该部分 还讨论了如何(可选)通过 相关。​​DataSourceTransactionManager​​​​DataSource​​​​HibernateTransactionManager​​​​SessionFactory​​​​TransactionManager​

1.3.1. 高级同步方法

首选方法是使用 Spring 最高级别的基于模板的持久性 集成 API 或将本机 ORM API 与事务感知工厂 Bean 一起使用,或 用于管理本机资源工厂的代理。这些事务感知解决方案 内部处理资源创建和重用、清理、可选事务 资源同步和异常映射。因此,用户数据访问代码确实 不必处理这些任务,但可以完全专注于非样板 持久性逻辑。通常,您使用本机ORM API或采用模板方法 对于 JDBC 访问,请使用。这些解决方案将在后续中详细介绍 部分。​​JdbcTemplate​

1.3.2. 低级同步方法

诸如(对于JDBC),(对于JPA),(对于Hibernate)等类存在于较低的级别。当您想要 直接处理本机持久性 API 的资源类型的应用程序代码, 您可以使用这些类来确保获得正确的 Spring 框架管理的实例, 事务(可选)同步,流程中发生的异常是 正确映射到一致的 API。​​DataSourceUtils​​​​EntityManagerFactoryUtils​​​​SessionFactoryUtils​

例如,在 JDBC 的情况下,而不是传统的 JDBC 调用方法 方法上,你可以改用 Spring 的类,如下所示:​​getConnection()​​​​DataSource​​​​org.springframework.jdbc.datasource.DataSourceUtils​

Connection conn = DataSourceUtils.getConnection(dataSource);

如果现有事务已具有同步(链接)的连接,则 返回实例。否则,方法调用将触发创建新的 连接,(可选)同步到任何现有事务并已建立 可用于同一事务中的后续重用。如前所述,anyis 包装在一个 Spring 框架中,一个 的 Spring 框架的未选中类型层次结构。这种方法 为您提供比从和轻松获得的更多信息 确保跨数据库甚至跨不同持久性技术的可移植性。​​SQLException​​​​CannotGetJdbcConnectionException​​​​DataAccessException​​​​SQLException​

这种方法也可以在没有 Spring 事务管理的情况下工作(事务 同步是可选的),因此无论您是否将 Spring 用于 事务管理。

当然,一旦你使用了 Spring 的 JDBC 支持、JPA 支持或 Hibernate 支持, 您通常不希望使用其他帮助程序类, 因为通过 Spring 抽象工作比直接工作更快乐 使用相关 API。例如,如果使用 Springorpackage 来简化 JDBC 的使用,则会发生正确的连接检索 在幕后,您无需编写任何特殊代码。​​DataSourceUtils​​​​JdbcTemplate​​​​jdbc.object​

1.3.3. ​​TransactionAwareDataSourceProxy​

在最低的层次上存在类。这是一个 目标的代理,它包装目标以增加对 弹簧管理的交易。在这方面,它类似于雅加达EE服务器提供的事务性JNDI。​​TransactionAwareDataSourceProxy​​​​DataSource​​​​DataSource​​​​DataSource​

你几乎永远不需要或想要使用这个类,除非存在 必须调用代码并传递标准的 JDBC接口实现。在 在这种情况下,此代码可能可用,但正在参与 Spring 托管 交易。可以使用更高级别编写新代码 前面提到的抽象。​​DataSource​

1.4. 声明式事务管理

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

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

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

1.4.1. 理解 Spring 框架的声明式事务实现

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

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

Spring 框架提供事务管理 命令式和响应式编程模型。拦截器检测所需的风味 通过检查方法返回类型进行事务管理。返回反应式的方法 诸如 Kotlin 的类型(或其中的子类型)有资格进行反应 事务管理。所有其他返回类型,包括使用 命令式事务管理。​​TransactionInterceptor​​​​Publisher​​​​Flow​​​​void​

事务管理风格会影响需要哪个事务管理器。祈使的 事务需要 A,而响应式事务使用实现。​​PlatformTransactionManager​​​​ReactiveTransactionManager​

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

Spring数据访问和数据访问层与业务或服务层之间的交互_应用程序_02

1.4.2. 声明式事务实现示例

请考虑以下接口及其伴随的实现。此示例使用 andclasses 作为占位符,以便您可以专注于事务 不关注特定域模型的使用情况。就本例而言, TheClass 在每个实现方法的主体中抛出实例这一事实很好。该行为可让您看到 正在创建事务,然后回滚以响应实例。以下清单显示了界面:​​Foo​​​​Bar​​​​DefaultFooService​​​​UnsupportedOperationException​​​​UnsupportedOperationException​​​​FooService​

// the service interface that we want to make transactional

package x.y.service;

public interface FooService

Foo getFoo(String fooName);

Foo getFoo(String fooName, String barName);

void insertFoo(Foo foo);

void updateFoo(Foo foo);

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

package x.y.service;

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)​​​​insertFoo(Foo)​​​​updateFoo(Foo)​

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

<!-- dont 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, dont forget the TransactionManager -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>

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

</beans>

检查上述配置。它假定您要创建一个服务对象, 底豆,事务性。要应用的事务语义已封装 在定义中。该定义读作“所有方法 从 ARE 开始在只读事务的上下文中运行,并且所有 其他方法是使用默认事务语义运行”。标签的属性设置为将驱动事务的bean的名称(在本例中为bean)。​​fooService​​​​<tx:advice/>​​​​<tx:advice/>​​​​get​​​​transaction-manager​​​​<tx:advice/>​​​​TransactionManager​​​​txManager​

该定义确保由 bean 定义的事务建议在程序中的适当点运行。首先,定义一个 与接口中定义的任何操作的执行相匹配的切入点 ().然后,您将切入点与 theby 使用 顾问。结果表明,在执行 a 时, 定义 by 的建议运行。​​<aop:config/>​​​​txAdvice​​​​FooService​​​​fooServiceOperation​​​​txAdvice​​​​fooServiceOperation​​​​txAdvice​

元素中定义的表达式是AspectJ切入点 表达。有关切入点的更多详细信息,请参阅AOP 部分 春天的表情。​​<aop:pointcut/>​

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

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

现在我们已经分析了配置,您可能会问自己, “所有这些配置实际上有什么作用?”

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

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());

运行上述程序的输出应类似于以下内容(Log4J 为清楚起见,来自 thePose by Theclass 方法的输出和堆栈跟踪已被截断):​​UnsupportedOperationException​​​​insertFoo(..)​​​​DefaultFooService​

<!-- the Spring container is starting up... -->
[AspectJInvocationContextExposingAdvisorAutoProxyCreator] - Creating implicit proxy for bean fooService with 0 common interceptors and 1 specific interceptors

<!-- the DefaultFooService is actually proxied -->
[JdkDynamicAopProxy] - Creating JDK dynamic proxy for [x.y.service.DefaultFooService]

<!-- ... the insertFoo(..) method is now being invoked on the proxy -->
[TransactionInterceptor] - Getting transaction for x.y.service.FooService.insertFoo

<!-- the transactional advice kicks in here... -->
[DataSourceTransactionManager] - Creating new transaction with name [x.y.service.FooService.insertFoo]
[DataSourceTransactionManager] - Acquired Connection [org.apache.commons.dbcp.PoolableConnection@a53de4] for JDBC transaction

<!-- the insertFoo(..) method from DefaultFooService throws an exception... -->
[RuleBasedTransactionAttribute] - Applying rules to determine whether transaction should rollback on java.lang.UnsupportedOperationException
[TransactionInterceptor] - Invoking rollback for transaction on x.y.service.FooService.insertFoo due to throwable [java.lang.UnsupportedOperationException]

<!-- and the transaction is rolled back (by default, RuntimeException instances cause rollback) -->
[DataSourceTransactionManager] - Rolling back JDBC transaction on Connection [org.apache.commons.dbcp.PoolableConnection@a53de4]
[DataSourceTransactionManager] - Releasing JDBC Connection after transaction
[DataSourceUtils] - Returning JDBC Connection to DataSource

Exception in thread "main" java.lang.UnsupportedOperationException at x.y.service.DefaultFooService.insertFoo(DefaultFooService.java:14)
<!-- AOP infrastructure stack trace elements removed for clarity -->
at $Proxy0.insertFoo(Unknown Source)
at Boot.main(Boot.java:11)

要使用反应式事务管理,代码必须使用反应式类型。

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

// the reactive service interface that we want to make transactional

package x.y.service;

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);

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

package x.y.service;

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​

反应式事务管理的另一个方面与数据转义有关,即 编程模型的自然结果。

命令性事务的方法返回值从事务方法返回 成功终止方法后,使部分计算的结果不会逃逸 方法闭包。

反应式事务方法返回一个反应式包装器类型,该类型表示 计算序列以及开始和完成计算的承诺。

Acan 可以在事务正在进行但不一定完成时发出数据。 因此,依赖于成功完成整个事务的方法需要 以确保调用代码中的完成和缓冲结果。​​Publisher​

1.4.3. 回滚声明式事务

上一节概述了如何为 类,通常是服务层类,在应用程序中以声明方式提供。本节 描述如何在简单的声明性中控制事务的回滚 XML 配置中的时尚。有关以声明方式控制回滚语义的详细信息 使用注释,请参阅@Transactional设置。​​@Transactional​

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

在其默认配置中,Spring 框架的事务基础结构代码 仅在运行时、未经检查的异常情况下将事务标记为回滚。 也就是说,当抛出的异常是 的实例或子类时。 (默认情况下,实例也会导致回滚)。已检查的异常 从事务方法引发不会导致默认回滚 配置。​​RuntimeException​​​​Error​

您可以准确配置哪些类型将事务标记为回滚, 通过指定回滚规则包括已检查的异常。​​Exception​

回滚规则

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

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

使用异常类型定义回滚规则时,该类型将用于匹配 针对抛出的异常类型及其超类型,提供类型安全和 避免使用模式时可能发生的任何意外匹配。例如,一个 值将仅匹配抛出的异常 类型及其子类。​​jakarta.servlet.ServletException.class​​​​jakarta.servlet.ServletException​

使用例外模式定义回滚规则时,该模式可以是完全 异常类型的限定类名或完全限定类名的子字符串 (必须是 的子类),目前没有通配符支持。为 例如,一个值 oforwill 匹配及其子类。​​Throwable​​​​"jakarta.servlet.ServletException"​​​​"ServletException"​​​​jakarta.servlet.ServletException​

以下 XML 代码段演示如何为选中的、 应用程序特定类型,通过属性提供异常模式:​​Exception​​​​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="updateStock" no-rollback-for="InstrumentNotFoundException"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>

当 Spring 框架的事务基础结构捕获异常并查询 配置的回滚规则,以确定是否将事务标记为回滚, 最强的匹配规则获胜。因此,在以下配置的情况下,任何 除 an 以外的异常导致回滚 服务员交易:​​InstrumentNotFoundException​

<tx:advice id="txAdvice">
<tx:attributes>
<tx:method name="*" rollback-for="Throwable" no-rollback-for="InstrumentNotFoundException"/>
</tx:attributes>
</tx:advice>

还可以以编程方式指示所需的回滚。虽然

以上是关于Spring数据访问和数据访问层与业务或服务层之间的交互的主要内容,如果未能解决你的问题,请参考以下文章

Spring数据访问和数据访问层与业务或服务层之间的交互

将数据访问层与服务层分开是否很好[关闭]

JDBC的架构设计

微服务定义

Dao层Dao层实现类和Service层Service实现类的关系

用于业务逻辑或数据访问层的 Web 服务