Introduction to Transactions in Java and Spring – Java和Spring中的事务介绍

最后修改: 2020年 8月 4日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.绪论

In this tutorial, we’ll understand what is meant by transactions in Java. Thereby we’ll understand how to perform resource local transactions and global transactions. This will also allow us to explore different ways to manage transactions in Java and Spring.

在本教程中,我们将了解Java中的事务是什么意思。因此,我们将了解如何执行资源本地事务和全局事务。这也将使我们能够探索在Java和Spring中管理事务的不同方法。

2. What Is a Transaction?

2.什么是事务?

Transactions in Java, as in general refer to a series of actions that must all complete successfully. Hence, if one or more action fails, all other actions must back out leaving the state of the application unchanged. This is necessary to ensure that the integrity of the application state is never compromised.

在Java中,交易一般是指一系列必须全部成功完成的操作。因此,如果一个或多个动作失败,所有其他动作必须退出,使应用程序的状态保持不变。这对于确保应用程序状态的完整性不被破坏是必要的。

Also, these transactions may involve one or more resources like database, message queue, giving rise to different ways to perform actions under a transaction. These include performing resource local transactions with individual resources. Alternatively, multiple resources can participate in a global transaction.

而且,这些事务可能涉及一个或多个资源,如数据库、消息队列,从而产生了不同的方式来执行事务下的行动。这些包括与单个资源执行资源本地交易。或者,多个资源可以参与到一个全局事务中。

3. Resource Local Transactions

3.资源本地事务

We’ll first explore how can we use transactions in Java while working with individual resources. Here, we may have multiple individual actions that we perform with a resource like a database. But, we may want them to happen as a unified whole, as in an indivisible unit of work. In other words, we want these actions to happen under a single transaction.

我们将首先探讨如何在Java中使用事务,同时对单个资源进行处理。在这里,我们可能有多个单独的动作,我们对一个资源(如数据库)执行。但是,我们可能希望它们作为一个统一的整体发生,就像一个不可分割的工作单元。换句话说,我们希望这些行动发生在一个单一的事务中。

In Java, we have several ways to access and operate on a resource like a database. Hence, the way we deal with transactions is also not the same. In this section, we’ll find how we can use transactions with some of these libraries in Java which are quite often used.

在Java中,我们有几种方法来访问和操作像数据库这样的资源。因此,我们处理事务的方式也是不一样的。在这一节中,我们会发现我们如何用Java中的一些相当常用的库来使用事务。

3.1. JDBC

3.1.JDBC

Java Database Connectivity (JDBC) is the API in Java that defines how to access databases in Java. Different database vendors provide JDBC drivers for connecting to the database in a vendor-agnostic manner. So, we retrieve a Connection from a driver to perform different operations on the database:

Java数据库连接(JDBC)Java中的API,定义了如何在Java中访问数据库。不同的数据库供应商提供JDBC驱动程序,用于以一种与供应商无关的方式连接到数据库。因此,我们从驱动程序中获取一个Connection,以对数据库进行不同的操作。

JDBC provides us the options to execute statements under a transaction. The default behavior of a Connection is auto-commit. To clarify, what this means is that every single statement is treated as a transaction and is automatically committed right after execution.

JDBC为我们提供了在事务中执行语句的选项。一个连接的默认行为是自动提交。澄清一下,这意味着每条语句都被当作一个事务,并在执行后自动提交。

However, if we wish to bundle multiple statements in a single transaction, this is possible to achieve as well:

然而,如果我们希望在一个交易中捆绑多个语句,这也是可以实现的。

Connection connection = DriverManager.getConnection(CONNECTION_URL, USER, PASSWORD);
try {
    connection.setAutoCommit(false);
    PreparedStatement firstStatement = connection .prepareStatement("firstQuery");
    firstStatement.executeUpdate();
    PreparedStatement secondStatement = connection .prepareStatement("secondQuery");
    secondStatement.executeUpdate();
    connection.commit();
} catch (Exception e) {
    connection.rollback();
}

Here, we have disabled the auto-commit mode of Connection. Hence, we can manually define the transaction boundary and perform a commit or rollback. JDBC also allows us to set a Savepoint that provides us more control over how much to rollback.

在这里,我们已经禁用了Connection的自动提交模式。因此,我们可以手动定义事务边界并执行commitrollback。JDBC还允许我们设置一个Savepoint,为我们提供对回滚程度的更多控制。

3.2. JPA

3.2 JPA

Java Persistence API (JPA) is a specification in Java that can be used to bridge the gap between object-oriented domain models and relational database systems. So, there are several implementations of JPA available from third parties like Hibernate, EclipseLink, and iBatis.

Java Persistence API(JPA)是Java中的一个规范,可用于弥合面向对象的领域模型和关系数据库系统之间的差距。因此,有一些第三方提供的JPA的实现,如Hibernate、EclipseLink和iBatis。

In JPA, we can define regular classes as an Entity that provides them persistent identity. The EntityManager class provides the necessary interface to work with multiple entities within a persistence context. The persistence context can be thought of as a first-level cache where entities are managed:

在JPA中,我们可以将普通的类定义为Entity,为它们提供持久化身份。EntityManager类提供了必要的接口,以便在一个持久化上下文中与多个实体一起工作。持久化上下文可以被认为是管理实体的第一级缓存。

JPA Architecture

JPA架构

The persistence context here can be of two types, transaction-scoped or extended-scoped. A transaction-scoped persistence context is bound to a single transaction. While the extended-scoped persistence context can span across multiple transactions. The default scope of a persistence context is transaction-scope.

这里的持久化上下文可以有两种类型,交易范围或扩展范围。一个事务范围的持久化上下文被绑定到一个单一的事务。而扩展范围的持久化上下文可以跨越多个事务。一个持久化上下文的默认范围是事务范围

Let’s see how can we create an EntityManager and define a transaction boundary manually:

让我们看看如何创建一个EntityManager并手动定义一个事务边界。

EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("jpa-example");
EntityManager entityManager = entityManagerFactory.createEntityManager();
try {
    entityManager.getTransaction().begin();
    entityManager.persist(firstEntity);
    entityManager.persist(secondEntity);
    entityManager.getTransaction().commit();
} catch (Exception e) {
    entityManager.getTransaction().rollback();
}

Here, we’re creating an EntityManager from EntityManagerFactory within the context of a transaction-scoped persistence context. Then we’re defining the transaction boundary with begin, commit, and rollback methods.

在这里,我们从EntityManagerFactory中创建一个EntityManager,并在一个事务范围的持久化上下文中创建。然后我们用begincommitrollback方法来定义事务边界。

3.3. JMS

3.3 JMS

Java Messaging Service (JMS) is a specification in Java that allows applications to communicate asynchronously using messages. The API allows us to create, send, receive, and read messages from a queue or topic. There are several messaging services that conform to JMS specifications including OpenMQ, and ActiveMQ.

Java消息传递服务(JMS)是Java中的一种规范,允许应用程序使用消息进行异步通信。该API允许我们从队列或主题中创建、发送、接收和读取消息。有几个符合JMS规范的消息传递服务,包括OpenMQ和ActiveMQ。

The JMS API supports bundling multiple send or receives operations in a single transaction. However, by the nature of message-based integration architecture, production and consumption of a message cannot be part of the same transaction. The scope of the transaction remains between the client and the JMS provider:

JMS API支持在一个事务中捆绑多个发送或接收操作。然而,根据基于消息的集成架构的性质,消息的生产和消费不能成为同一事务的一部分。事务的范围仍然在客户和JMS提供者之间。


JMS allows us to create a Session from a Connection that we obtain from a vendor-specific ConnectionFactory. We have an option to create a Session that is transacted or not. For non-transaction Sessions, we can further define an appropriate acknowledge mode as well.


JMS允许我们从供应商特定的ConnectionFactory获得的Connection创建一个Session。我们有一个选项,可以创建一个有交易或没有交易的Session。对于非交易的Sessions我们也可以进一步定义一个适当的确认模式。

Let’s see how we can create a transacted Session to send multiple messages under a transaction:

让我们看看我们如何创建一个事务性的Session来发送一个事务下的多个消息。

ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(CONNECTION_URL);
Connection connection = = connectionFactory.createConnection();
connection.start();
try {
    Session session = connection.createSession(true, 0);
    Destination = destination = session.createTopic("TEST.FOO");
    MessageProducer producer = session.createProducer(destination);
    producer.send(firstMessage);
    producer.send(secondMessage);
    session.commit();
} catch (Exception e) {
    session.rollback();
}

Here, we’re creating a MessageProducer for the Destination of the type of topic. We get the Destination from the Session we created earlier. We further use Session to define transaction boundaries using the methods commit and rollback.

在这里,我们要为话题类型的目的地创建一个MessageProducer。我们从之前创建的Session中获得Destination。我们进一步使用Session来定义事务边界,使用方法commitrollback

4. Global Transactions

4.全球事务

As we saw resource local transactions allow us to perform multiple operations within a single resource as a unified whole. But, quite often we deal with operations that span across multiple resources. For instance, operation in two different databases or a database and a message queue. Here, local transaction support within resources will not be sufficient for us.

正如我们所看到的,资源本地事务允许我们在一个单一的资源中作为一个统一的整体执行多个操作。但是,我们经常要处理跨越多个资源的操作。例如,在两个不同的数据库或一个数据库和一个消息队列中的操作。在这里,资源内的本地事务支持对我们来说是不够的。

What we need in these scenarios is a global mechanism to demarcate transactions spanning multiple participating resources. This is often known as distributed transactions and there are specifications that have been proposed to deal with them effectively.

在这些情况下,我们需要的是一种全局机制来划分跨越多个参与资源的事务。这通常被称为分布式交易,并且有一些规范被提出来有效地处理它们。

The XA Specification is one such specification which defines a transaction manager to control transaction across multiple resources. Java has quite mature support for distributed transactions conforming to the XA Specification through the components JTA and JTS.

XA规范就是这样一个规范,它定义了一个交易管理器,以控制跨多个资源的交易。Java通过JTA和JTS组件对符合XA规范的分布式事务有相当成熟的支持。

4.1. JTA

4.1 JTA

Java Transaction API (JTA) is a Java Enterprise Edition API developed under the Java Community Process. It enables Java applications and application servers to perform distributed transactions across XA resources. JTA is modeled around XA architecture, leveraging two-phase commit.

Java Transaction API(JTA)是在Java Community Process下开发的一个Java企业版API。它使Java应用程序和应用服务器能够在XA资源上执行分布式事务。JTA是围绕XA架构建立的,利用了两阶段提交。

JTA specifies standard Java interfaces between a transaction manager and the other parties in a distributed transaction:

JTA规定了交易管理器和分布式交易中其他各方之间的标准Java接口。

Let’s understand some of the key interfaces highlighted above:

让我们了解一下上面强调的一些关键界面。

  • TransactionManager: An interface which allows an application server to demarcate and control transactions
  • UserTransaction: This interface allows an application program to demarcate and control transactions explicitly
  • XAResource: The purpose of this interface is to allow a transaction manager to work with resource managers for XA-compliant resources

4.2. JTS

4.2 JTS

Java Transaction Service (JTS) is a specification for building the transaction manager that maps to the OMG OTS specification. JTS uses the standard CORBA ORB/TS interfaces and Internet Inter-ORB Protocol (IIOP) for transaction context propagation between JTS transaction managers.

Java事务服务(JTS)构建事务管理器的规范,该规范映射到OMG OTS规范。JTS使用标准的CORBA ORB/TS接口和Internet Inter-ORB协议(IIOP)来实现JTS事务管理器之间的事务上下文传播。

At a high level, it supports the Java Transaction API (JTA). A JTS transaction manager provides transaction services to the parties involved in a distributed transaction:

在高层次上,它支持Java Transaction API(JTA)。JTS事务管理器向参与分布式事务的各方提供事务服务。

Services that JTS provides to an application are largely transparent and hence we may not even notice them in the application architecture. JTS is architected around an application server which abstracts all transaction semantics from the application programs.

JTS为应用程序提供的服务在很大程度上是透明的,因此我们甚至可能不会在应用程序架构中注意到它们。JTS是围绕着一个应用服务器架构的,它从应用程序中抽象出所有的交易语义。

5. JTA Transaction Management

5.JTA事务管理

Now it’s time to understand how we can manage a distributed transaction using JTA. Distributed transactions are not trivial solutions and hence have cost implications as well. Moreover, there are multiple options that we can choose from to include JTA in our application. Hence, our choice must be in the view of overall application architecture and aspirations.

现在是时候了解我们如何使用JTA来管理一个分布式事务了。分布式事务并不是微不足道的解决方案,因此也会产生成本问题。此外,我们可以选择多种方案将JTA纳入我们的应用程序中。因此,我们的选择必须考虑到整个应用架构和愿望。

5.1. JTA in Application Server

5.1.应用服务器中的JTA

As we have seen earlier, JTA architecture relies on the application server to facilitate a number of transaction-related operations. One of the key services it relies on the server to provide is a naming service through JNDI. This is where XA resources like data sources are bound to and retrieved from.

正如我们前面所看到的,JTA架构依赖于应用服务器来促进一些与交易相关的操作。它所依赖的服务器提供的关键服务之一是通过JNDI提供的命名服务。这是数据源等XA资源被绑定和检索的地方。

Apart from this, we have a choice in terms of how we want to manage the transaction boundary in our application. This gives rise to two types of transactions within the Java application server:

除此之外,我们还可以选择如何在我们的应用程序中管理事务边界。这在Java应用服务器中产生了两种类型的事务。

  • Container-managed Transaction: As the name suggests, here the transaction boundary is set by the application server. This simplifies the development of Enterprise Java Beans (EJB) as it does not include statements related to transaction demarcation and relies solely on the container to do so. However, this does not provide enough flexibility for the application.
  • Bean-managed Transaction: Contrary to the container-managed transaction, in a bean-managed transaction EJBs contain the explicit statements to define the transaction demarcation. This provides precise control to the application in marking the boundaries of the transaction, albeit at the cost of more complexity.

One of the main drawbacks of performing transactions in the context of an application server is that the application becomes tightly coupled with the server. This has implications with respect to testability, manageability, and portability of the application. This is more profound in microservice architecture where the emphasis is more on developing server-neutral applications.

在应用服务器背景下执行事务的主要缺点之一是,应用程序变得与服务器紧密耦合。这对应用程序的可测试性、可管理性和可移植性都有影响。这在微服务架构中更为深刻,因为该架构更强调开发服务器中立的应用程序。

5.2. JTA Standalone

5.2 JTA单机版

The problems we discussed in the last section have provided a huge momentum towards creating solutions for distributed transactions that does not rely on an application server. There are several options available to us in this regard, like using transaction support with Spring or use a transaction manager like Atomikos.

我们在上一节中讨论的问题为创建不依赖应用服务器的分布式事务解决方案提供了巨大的动力。在这方面,我们有几种选择,比如使用Spring的事务支持,或者使用Atomikos这样的事务管理器。

Let’s see how we can use a transaction manager like Atomikos to facilitate a distributed transaction with a database and a message queue. One of the key aspects of a distributed transaction is enlisting and delisting the participating resources with the transaction monitor. Atomikos takes care of this for us. All we have to do is use Atomikos-provided abstractions:

让我们来看看我们如何使用Atomikos这样的事务管理器来促进具有数据库和消息队列的分布式事务。分布式事务的一个关键方面是通过事务监视器将参与的资源列入名单和除名。Atomikos为我们解决了这个问题。我们所要做的就是使用Atomikos提供的抽象概念。

AtomikosDataSourceBean atomikosDataSourceBean = new AtomikosDataSourceBean();
atomikosDataSourceBean.setXaDataSourceClassName("com.mysql.cj.jdbc.MysqlXADataSource");
DataSource dataSource = atomikosDataSourceBean;

Here, we are creating an instance of AtomikosDataSourceBean and registering the vendor-specific XADataSource. From here on, we can continue using this like any other DataSource and get the benefits of distributed transactions.

在这里,我们正在创建一个AtomikosDataSourceBean的实例,并注册了供应商特定的XADataSource。从这里开始,我们可以继续像其他DataSource一样使用它,并获得分布式事务的好处。

Similarly, we have an abstraction for message queue which takes care of registering the vendor-specific XA resource with the transaction monitor automatically:

同样地,我们有一个消息队列的抽象,它负责将供应商特定的XA资源自动注册到事务监视器上。

AtomikosConnectionFactoryBean atomikosConnectionFactoryBean = new AtomikosConnectionFactoryBean();
atomikosConnectionFactoryBean.setXaConnectionFactory(new ActiveMQXAConnectionFactory());
ConnectionFactory connectionFactory = atomikosConnectionFactoryBean;

Here, we are creating an instance of AtomikosConnectionFactoryBean and registering the XAConnectionFactory from an XA-enabled JMS vendor. After this, we can continue to use this as a regular ConnectionFactory.

在这里,我们正在创建一个AtomikosConnectionFactoryBean的实例,并从一个支持XA的JMS供应商处注册XAConnectionFactory。在这之后,我们可以继续将其作为普通的ConnectionFactory使用。

Now, Atomikos provides us the last piece of the puzzle to bring everything together, an instance of UserTransaction:

现在,Atomikos为我们提供了最后一块拼图,将所有的东西整合在一起,一个UserTransaction的实例。

UserTransaction userTransaction = new UserTransactionImp();

Now, we are ready to create an application with distributed transaction spanning across our database and the message queue:

现在,我们已经准备好创建一个具有分布式事务的应用程序,横跨我们的数据库和消息队列。

try {
    userTransaction.begin();

    java.sql.Connection dbConnection = dataSource.getConnection();
    PreparedStatement preparedStatement = dbConnection.prepareStatement(SQL_INSERT);
    preparedStatement.executeUpdate();

    javax.jms.Connection mbConnection = connectionFactory.createConnection();
    Session session = mbConnection.createSession(true, 0);
    Destination destination = session.createTopic("TEST.FOO");
    MessageProducer producer = session.createProducer(destination);
    producer.send(MESSAGE);

    userTransaction.commit();
} catch (Exception e) {
    userTransaction.rollback();
}

Here, we are using the methods begin and commit in the class UserTransaction to demarcate the transaction boundary. This includes saving a record in the database as well as publishing a message to the message queue.

在这里,我们使用UserTransaction类中的方法begincommit来划定事务边界。这包括在数据库中保存一条记录,以及向消息队列发布一条消息。

6. Transactions Support in Spring

6.Spring的事务支持

We have seen that handling transactions are rather an involved task which includes a lot of boilerplate coding and configurations. Moreover, each resource has its own way of handling local transactions. In Java, JTA abstracts us from these variations but further brings provider-specific details and the complexity of the application server.

我们已经看到,处理事务是一项相当复杂的任务,包括大量的模板编码和配置。此外,每个资源都有自己的处理本地事务的方式。在Java中,JTA将我们从这些变化中抽象出来,但却进一步带来了供应商的特定细节和应用服务器的复杂性。

Spring platform provides us a much cleaner way of handling transactions, both resource local and global transactions in Java. This together with the other benefits of Spring creates a compelling case for using Spring to handle transactions. Moreover, it’s quite easy to configure and switch a transaction manager with Spring, which can be server provided or standalone.

Spring平台为我们提供了一种更简洁的处理事务的方式,包括Java中的资源本地和全局事务。这与Spring的其他优点一起,为使用Spring处理事务创造了一个令人信服的理由。此外,用Spring配置和切换一个事务管理器非常容易,它可以是服务器提供的,也可以是独立的。

Spring provides us this seamless abstraction by creating a proxy for the methods with transactional code. The proxy manages the transaction state on behalf of the code with the help of TransactionManager:

The central interface here is PlatformTransactionManager which has a number of different implementations available. It provides abstractions over JDBC (DataSource), JMS, JPA, JTA, and many other resources.

Spring为我们提供了这种无缝的抽象,为带有事务性代码的方法创建了一个代理。该代理在TransactionManager的帮助下代表代码管理事务状态:

这里的核心接口是PlatformTransactionManager,它有许多不同的实现。它提供了对JDBC(数据源)、JMS、JPA、JTA和许多其他资源的抽象。

6.1. Configurations

6.1.配置

Let’s see how we can configure Spring to use Atomikos as a transaction manager and provide transactional support for JPA and JMS. We’ll begin by defining a PlatformTransactionManager of the type JTA:

让我们看看如何配置Spring使用Atomikos作为事务管理器并为JPA和JMS提供事务性支持。我们将首先定义一个JTA类型的PlatformTransactionManager

@Bean
public PlatformTransactionManager platformTransactionManager() throws Throwable {
    return new JtaTransactionManager(
                userTransaction(), transactionManager());
}

Here, we are providing instances of UserTransaction and TransactionManager to JTATransactionManager. These instances are provided by a transaction manager library like Atomikos:

在这里,我们提供UserTransactionTransactionManager的实例给JTATransactionManager。这些实例是由Atomikos这样的事务管理器库提供的。

@Bean
public UserTransaction userTransaction() {
    return new UserTransactionImp();
}

@Bean(initMethod = "init", destroyMethod = "close")
public TransactionManager transactionManager() {
    return new UserTransactionManager();
}

The classes UserTransactionImp and UserTransactionManager are provided by Atomikos here.

Atomikos在这里提供了UserTransactionImpUserTransactionManager类。

Further, we need to define the JmsTemplete which the core class allowing synchronous JMS access in Spring:

此外,我们需要定义JmsTemplete,它是Spring中允许同步JMS访问的核心类。

@Bean
public JmsTemplate jmsTemplate() throws Throwable {
    return new JmsTemplate(connectionFactory());
}

Here, ConnectionFactory is provided by Atomikos where it enables distributed transaction for Connection provided by it:

这里,ConnectionFactory由Atomikos提供,它为Connection提供的分布式事务。

@Bean(initMethod = "init", destroyMethod = "close")
public ConnectionFactory connectionFactory() {
    ActiveMQXAConnectionFactory activeMQXAConnectionFactory = new 
ActiveMQXAConnectionFactory();
    activeMQXAConnectionFactory.setBrokerURL("tcp://localhost:61616");
    AtomikosConnectionFactoryBean atomikosConnectionFactoryBean = new AtomikosConnectionFactoryBean();
    atomikosConnectionFactoryBean.setUniqueResourceName("xamq");
    atomikosConnectionFactoryBean.setLocalTransactionMode(false);
atomikosConnectionFactoryBean.setXaConnectionFactory(activeMQXAConnectionFactory);
    return atomikosConnectionFactoryBean;
}

So, as we can see, here we are wrapping a JMS provider-specific XAConnectionFactory with AtomikosConnectionFactoryBean.

因此,我们可以看到,在这里我们用AtomikosConnectionFactoryBean包装了一个JMS提供者专用的XAConnectionFactory

Next, we need to define an AbstractEntityManagerFactoryBean that is responsible for creating JPA EntityManagerFactory bean in Spring:

接下来,我们需要定义一个AbstractEntityManagerFactoryBean,它负责在Spring中创建JPA EntityManagerFactory Bean。

@Bean
public LocalContainerEntityManagerFactoryBean entityManager() throws SQLException {
    LocalContainerEntityManagerFactoryBean entityManager = new LocalContainerEntityManagerFactoryBean();
    entityManager.setDataSource(dataSource());
    Properties properties = new Properties();
    properties.setProperty( "javax.persistence.transactionType", "jta");
    entityManager.setJpaProperties(properties);
    return entityManager;
}

As before, the DataSource that we set in the LocalContainerEntityManagerFactoryBean here is provided by Atomikos with distributed transactions enabled:

与之前一样,我们在LocalContainerEntityManagerFactoryBean中设置的DataSource由Atomikos提供,并启用了分布式事务。

@Bean(initMethod = "init", destroyMethod = "close")
public DataSource dataSource() throws SQLException {
    MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource();
    mysqlXaDataSource.setUrl("jdbc:mysql://127.0.0.1:3306/test");
    AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
    xaDataSource.setXaDataSource(mysqlXaDataSource);
    xaDataSource.setUniqueResourceName("xads");
    return xaDataSource;
}

Here again, we are wrapping the provider-specific XADataSource in AtomikosDataSourceBean.

在这里,我们再次将特定于提供者的XADataSource包裹在AtomikosDataSourceBean中。

6.2. Transaction Management

6.2.事务管理

Having gone through all the configurations in the last section, we must feel quite overwhelmed! We may even question the benefits of using Spring after all. But do remember that all this configuration has enabled us abstraction from most of the provider-specific boilerplate and our actual application code does not need to be aware of that at all.

在经历了上一节中的所有配置后,我们一定会感到相当的不知所措!我们甚至会质疑使用Spring的好处。我们甚至会质疑使用Spring的好处。但请记住,所有这些配置都使我们能够从大多数特定于提供者的模板中抽象出来,我们的实际应用代码根本不需要知道这些。

So, now we are ready to explore how to use transactions in Spring where we intend to update the database and publish messages. Spring provides us two ways to achieve this with their own benefits to choose from. Let’s understand how we can make use of them:

所以,现在我们准备探索如何在Spring中使用事务,我们打算在其中更新数据库和发布消息。Spring为我们提供了两种方法来实现这一目标,并有各自的好处可供选择。让我们了解一下我们如何利用它们。

  • Declarative Support

The easiest way to use transactions in Spring is with declarative support. Here, we have a convenience annotation available to be applied at the method or even at the class. This simply enables global transaction for our code:

在Spring中使用事务的最简单方法是声明性支持。在这里,我们有一个方便的注解,可以在方法甚至是类上应用。这只是为我们的代码启用了全局事务。

@PersistenceContext
EntityManager entityManager;

@Autowired
JmsTemplate jmsTemplate;

@Transactional(propagation = Propagation.REQUIRED)
public void process(ENTITY, MESSAGE) {
   entityManager.persist(ENTITY);
   jmsTemplate.convertAndSend(DESTINATION, MESSAGE);
}

The simple code above is sufficient to allow a save-operation in the database and a publish-operation in message queue within a JTA transaction.

上面的简单代码足以允许在一个JTA事务中在数据库中进行保存操作和在消息队列中进行发布操作。

  • Programmatic Support

While the declarative support is quite elegant and simple, it does not offer us the benefit of controlling the transaction boundary more precisely. Hence, if we do have a certain need to achieve that, Spring offers programmatic support to demarcate transaction boundary:

虽然声明式支持相当优雅和简单,但它并没有为我们提供更精确地控制事务边界的好处。因此,如果我们确实有某种需要,Spring提供了程序化的支持来划分事务边界。

@Autowired
private PlatformTransactionManager transactionManager;

public void process(ENTITY, MESSAGE) {
    TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
    transactionTemplate.executeWithoutResult(status -> {
        entityManager.persist(ENTITY);
        jmsTemplate.convertAndSend(DESTINATION, MESSAGE);
    });
}

So, as we can see, we have to create a TransactionTemplate with the available PlatformTransactionManager. Then we can use the TransactionTemplete to process a bunch of statements within a global transaction.

因此,正如我们所看到的,我们必须用可用的PlatformTransactionManager创建一个TransactionTemplate。然后我们可以使用TransactionTemplete来处理一个全局事务中的一堆语句。

7. Afterthoughts

7.事后感想

As we have seen that handling transactions, particularly those that span across multiple resources are complex. Moreover, transactions are inherently blocking which is detrimental to latency and throughput of an application. Further, testing and maintaining code with distributed transactions is not easy, especially if the transaction depends on the underlying application server. So, all in all, it’s best to avoid transactions at all if we can!

我们已经看到,处理事务,尤其是那些跨越多个资源的事务是很复杂的。此外,交易在本质上是阻塞的,这对应用程序的延迟和吞吐量是不利的。此外,测试和维护带有分布式事务的代码并不容易,尤其是当事务依赖于底层应用服务器的时候。所以,总而言之,如果可以的话,最好是完全避免交易。

But that is far from reality. In short, in real-world applications, we do often have a legitimate need for transactions. Although it’s possible to rethink the application architecture without transactions, it may not always be possible. Hence, we must adopt certain best practices when working with transactions in Java to make our applications better:

但这与现实相去甚远。简而言之,在现实世界的应用中,我们确实经常有对事务的合法需求。尽管我们有可能重新思考没有事务的应用架构,但这未必总是可能的。因此,在Java中使用事务时,我们必须采用某些最佳实践,以使我们的应用程序变得更好。

  • One of the fundamental shifts we should adopt is to use standalone transaction managers instead of those provided by an application server. This alone can simplify our application greatly. Moreover, it’s much suited for cloud-native microservice architecture.
  • Further, an abstraction layer like Spring can help us contain the direct impact of providers like JPA or JTA providers. So, this can enable us to switch between providers without much impact on our business logic. Moreover, it takes away the low-level responsibilities of managing the transaction state from us.
  • Lastly, we should be careful in picking the transaction boundary in our code. Since transactions are blocking, it’s always better to keep the transaction boundary as restricted as possible. If necessary we should prefer programmatic over declarative control for transactions.

8. Conclusion

8.结语

To sum up, in this tutorial we discussed transactions in the context of Java. We went through support for individual resource local transactions in Java for different resources. We also went through the ways to achieve global transactions in Java.

综上所述,在本教程中,我们讨论了Java背景下的事务。我们经历了Java中对不同资源的本地事务的支持。我们还经历了在Java中实现全局事务的方法。

Further, we went through different ways to manage global transactions in Java. Also, we understood how Spring makes using transactions in Java easier for us.

此外,我们还学习了在Java中管理全局事务的不同方法。此外,我们还了解了Spring是如何让我们在Java中使用事务的。

Finally, we went through some of the best practices when working with transactions in Java.

最后,我们回顾了在Java中处理事务时的一些最佳实践。