Understanding the Spring Framework Transaction Abstraction
Spring 事务抽象的关键是一个事务策略的概念。事务策略由 TransactionManager
定义,特别是 org.springframework.transaction.PlatformTransactionManager
接口用于命令式事务管理和 org.springframework.transaction.ReactiveTransactionManager
接口用于响应式事务管理。以下列表展示了 PlatformTransactionManager
API 的定义:
The key to the Spring transaction abstraction is the notion of a transaction strategy. A
transaction strategy is defined by a TransactionManager
, specifically the
org.springframework.transaction.PlatformTransactionManager
interface for imperative
transaction management and the
org.springframework.transaction.ReactiveTransactionManager
interface for reactive
transaction management. The following listing shows the definition of the
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;
}
这主要是一个服务提供程序接口(SPI),尽管你可以从应用程序代码中programmatically使用它。由于`PlatformTransactionManager`是一个接口,因此可以根据需要轻松地模拟或存根。它不与查找策略(例如JNDI)绑定。`PlatformTransactionManager`实现被定义为Spring Framework IoC容器中的任何其他对象(或bean)。仅此优点就使Spring Framework事务成为一项有价值的抽象,即使在你使用JTA时也是如此。你可以比直接使用JTA更轻松地测试事务代码。
This is primarily a service provider interface (SPI), although you can use it
programmatically from your application code. Because
PlatformTransactionManager
is an interface, it can be easily mocked or stubbed as
necessary. It is not tied to a lookup strategy, such as JNDI.
PlatformTransactionManager
implementations are defined like any other object (or bean)
in the Spring Framework IoC container. This benefit alone makes Spring Framework
transactions a worthwhile abstraction, even when you work with JTA. You can test
transactional code much more easily than if it used JTA directly.
同样,根据 Spring 的理念,TransactionException
可能被任何 TransactionManager
接口的方法抛出,它未被签入(也就是,它扩展了 java.lang.RuntimeException
类)。事务基础设施故障几乎都是致命的。在应用程序代码可以实际从事务故障中恢复的罕见情况下,应用程序开发人员仍然可以选择来捕获并处理 TransactionException
。关键点是开发人员并非_被迫_去做这件事。
Again, in keeping with Spring’s philosophy, the TransactionException
that can be thrown
by any of the PlatformTransactionManager
interface’s methods is unchecked (that
is, it extends the java.lang.RuntimeException
class). Transaction infrastructure
failures are almost invariably fatal. In rare cases where application code can actually
recover from a transaction failure, the application developer can still choose to catch
and handle TransactionException
. The salient point is that developers are not
forced to do so.
getTransaction(..)
方法返回一个 TransactionStatus
对象,具体取决于一个 TransactionDefinition
参数。返回的 TransactionStatus
可能代表一个新的事务,也可以代表一个现有的事务(如果在当前调用栈中存在一个匹配的事务)。在后一种情况下的含义是,与 Jakarta EE 事务上下文一样,一个 TransactionStatus
与一个执行线程相关联。
The getTransaction(..)
method returns a TransactionStatus
object, depending on a
TransactionDefinition
parameter. The returned TransactionStatus
might represent a
new transaction or can represent an existing transaction, if a matching transaction
exists in the current call stack. The implication in this latter case is that, as with
Jakarta EE transaction contexts, a TransactionStatus
is associated with a thread of
execution.
从 Spring Framework 5.2 开始,Spring 还为使用响应式类型或 Kotlin Coroutine 的响应式应用程序提供了一个事务管理抽象。以下列表展示了由 org.springframework.transaction.ReactiveTransactionManager
定义的事务策略:
As of Spring Framework 5.2, Spring also provides a transaction management abstraction for
reactive applications that make use of reactive types or Kotlin Coroutines. The following
listing shows the transaction strategy defined by
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),尽管你可以从应用程序代码中programmatically使用它。由于`ReactiveTransactionManager`是一个接口,因此可以根据需要轻松地模拟或存根。
The reactive transaction manager is primarily a service provider interface (SPI),
although you can use it programmatically from your
application code. Because ReactiveTransactionManager
is an interface, it can be easily
mocked or stubbed as necessary.
TransactionDefinition
接口规定:
The TransactionDefinition
interface specifies:
-
Propagation: Typically, all code within a transaction scope runs in that transaction. However, you can specify the behavior if a transactional method is run when a transaction context already exists. For example, code can continue running in the existing transaction (the common case), or the existing transaction can be suspended and a new transaction created. Spring offers all of the transaction propagation options familiar from EJB CMT. To read about the semantics of transaction propagation in Spring, see Transaction Propagation.
-
Isolation: The degree to which this transaction is isolated from the work of other transactions. For example, can this transaction see uncommitted writes from other transactions?
-
Timeout: How long this transaction runs before timing out and being automatically rolled back by the underlying transaction infrastructure.
-
Read-only status: You can use a read-only transaction when your code reads but does not modify data. Read-only transactions can be a useful optimization in some cases, such as when you use Hibernate.
这些设置体现了标准事务概念。如有必要,请参阅讨论事务隔离级别和其他核心事务概念的资源。了解这些概念对于使用 Spring 框架或任何事务管理解决方案至关重要。
These settings reflect standard transactional concepts. If necessary, refer to resources that discuss transaction isolation levels and other core transaction concepts. Understanding these concepts is essential to using the Spring Framework or any transaction management solution.
TransactionStatus
接口为事务代码提供了一种简单的方法,用于控制事务执行和查询事务状态。这些概念应该是熟悉的,因为它们对所有事务 API 都是通用的。以下清单显示了 TransactionStatus
接口:
The TransactionStatus
interface provides a simple way for transactional code to
control transaction execution and query transaction status. The concepts should be
familiar, as they are common to all transaction APIs. The following listing shows the
TransactionStatus
interface:
public interface TransactionStatus extends TransactionExecution, SavepointManager, Flushable {
@Override
boolean isNewTransaction();
boolean hasSavepoint();
@Override
void setRollbackOnly();
@Override
boolean isRollbackOnly();
void flush();
@Override
boolean isCompleted();
}
无论你选择在 Spring 中采用声明式事务管理还是程序化事务管理,定义正确的 TransactionManager
实现都是至关重要的。你通常通过依赖项注入来定义此实现。
Regardless of whether you opt for declarative or programmatic transaction management in
Spring, defining the correct TransactionManager
implementation is absolutely essential.
You typically define this implementation through dependency injection.
TransactionManager
的实现通常需要了解其所处理的环境:JDBC、JTA、Hibernate 等。以下示例展示了如何定义一个本地 PlatformTransactionManager
实现(本例中使用纯 JDBC)。
TransactionManager
implementations normally require knowledge of the environment in
which they work: JDBC, JTA, Hibernate, and so on. The following examples show how you can
define a local PlatformTransactionManager
implementation (in this case, with plain
JDBC.)
你可以通过创建一个类似于以下 Bean 来定义一个 JDBC DataSource
:
You can define a JDBC DataSource
by creating a bean similar to the following:
<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>
然后,相关的 PlatformTransactionManager
Bean 定义引用 DataSource
定义。它应该类似于以下示例:
The related PlatformTransactionManager
bean definition then has a reference to the
DataSource
definition. It should resemble the following example:
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
如果你在 Jakarta EE 容器中使用 JTA,那么可以使用容器 DataSource
,通过 JNDI 获取,结合 Spring 的 JtaTransactionManager
一同使用。以下示例展示了 JTA 和 JNDI 的查找版本:
If you use JTA in a Jakarta EE container, then you use a container DataSource
, obtained
through JNDI, in conjunction with Spring’s JtaTransactionManager
. The following example
shows what the JTA and JNDI lookup version would look like:
<?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
(或任何其他特定资源),因为它使用容器的全局事务管理基础设施。
The JtaTransactionManager
does not need to know about the DataSource
(or any other
specific resources) because it uses the container’s global transaction management
infrastructure.
|
The preceding definition of the |
如果您使用 JTA,则事务管理器定义应一致,无论您使用哪种数据访问技术,无论是 JDBC、Hibernate JPA 还是任何其他受支持的技术。这是因为 JTA 事务是全局事务,可以列出任何事务性资源。 |
If you use JTA, your transaction manager definition should look the same, regardless of what data access technology you use, be it JDBC, Hibernate JPA, or any other supported technology. This is due to the fact that JTA transactions are global transactions, which can enlist any transactional resource. |
在所有 Spring 事务设置中,应用程序代码无需更改。你可以通过简单地更改配置来更改事务的管理方式,即使更改意味着从本地事务移动到全局事务,反之亦然。
In all Spring transaction setups, application code does not need to change. You can change how transactions are managed merely by changing configuration, even if that change means moving from local to global transactions or vice versa.
Hibernate Transaction Setup
你还可以轻松使用 Hibernate 本地事务,如以下示例所示。在这种情况下,你需要定义一个 Hibernate LocalSessionFactoryBean
,你的应用程序代码可以使用它来获取 Hibernate Session
实例。
You can also easily use Hibernate local transactions, as shown in the following examples.
In this case, you need to define a Hibernate LocalSessionFactoryBean
, which your
application code can use to obtain Hibernate Session
instances.
DataSource
Bean 定义与前面所示的本地 JDBC 示例类似,因此以下示例中不会显示它。
The DataSource
bean definition is similar to the local JDBC example shown previously
and, thus, is not shown in the following example.
如果 |
If the |
在这种情况下,txManager
Bean 是 HibernateTransactionManager
类型。正如 DataSourceTransactionManager
需要引用 DataSource
一样,HibernateTransactionManager
需要引用 SessionFactory
。以下示例声明了 sessionFactory
和 txManager
Bean:
The txManager
bean in this case is of the HibernateTransactionManager
type. In the
same way as the DataSourceTransactionManager
needs a reference to the DataSource
, the
HibernateTransactionManager
needs a reference to the SessionFactory
. The following
example declares sessionFactory
and txManager
beans:
<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 示例中相同的 JtaTransactionManager
,如下面的示例所示。此外,建议通过事务协调器和可能的连接释放模式配置使 Hibernate 了解 JTA:
If you use Hibernate and Jakarta EE container-managed JTA transactions, you should use the
same JtaTransactionManager
as in the previous JTA example for JDBC, as the following
example shows. Also, it is recommended to make Hibernate aware of JTA through its
transaction coordinator and possibly also its connection release mode configuration:
<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"/>
或者,你也可以将 JtaTransactionManager
传递到你的 LocalSessionFactoryBean
,以强制实施相同的默认值:
Or alternatively, you may pass the JtaTransactionManager
into your LocalSessionFactoryBean
for enforcing the same defaults:
<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"/>