1. Introduction
1.绪论
Atomikos is a transaction library for Java applications. In this tutorial, we’ll understand why and how to use Atomikos.
Atomikos是用于Java应用程序的事务库。在本教程中,我们将了解为什么以及如何使用Atomikos。
In the process, we’ll also go through the basics of transactions and why we need them.
在这个过程中,我们还将了解交易的基本情况以及为什么我们需要交易。
Then, we’ll create a simple application with transactions leveraging different APIs from Atomikos.
然后,我们将创建一个简单的应用程序,利用Atomikos的不同API进行交易。
2. Understanding the Basics
2.了解基础知识
Before we discuss Atomikos, let’s understand what exactly transactions are and a few concepts related to them. Put simply, a transaction is a logical unit of work whose effect is visible outside the transaction either in entirety or not at all.
在我们讨论Atomikos之前,让我们先了解一下交易到底是什么,以及与之相关的一些概念。简单地说,事务是一个工作的逻辑单位,其效果在事务之外是可见的,要么是完全可见,要么是完全不可见。
Let’s take an example to understand this better. A typical retail application reserves the inventory and then places an order:
让我们举一个例子来更好地理解这一点。一个典型的零售应用保留库存,然后下订单。
Here, we’d like these two operations to either happen together or not happen at all. We can achieve this by wrapping these operations into a single transaction.
在这里,我们希望这两个操作要么一起发生,要么根本就不发生。我们可以通过将这些操作包装成一个单一的事务来实现这一点。
2.1. Local vs. Distributed Transaction
2.1.本地事务与分布式事务
A transaction can involve multiple independent operations. These operations can execute on the same resource or different resources. We refer to the participating components in a transaction like a database as a resource here.
一个事务可以涉及多个独立的操作。这些操作可以在同一资源或不同资源上执行。我们在这里把数据库等参与交易的组件称为资源。
Transactions within a single resource are known local transaction while those spawning across multiple resources are known as the distributed transaction:
单一资源内的交易被称为本地交易,而跨越多个资源产生的交易则被称为分布式交易。
Here, inventory and orders can be two tables in the same database, or they can be two different databases — possibly running on different machines altogether.
在这里,库存和订单可以是同一个数据库中的两个表,也可以是两个不同的数据库–可能完全在不同的机器上运行。
2.2. XA Specification and Java Transaction API
2.2.XA规范和Java事务API
XA refers to eXtended Architecture, which is a specification for distributed transaction processing. The goal of XA is to provide atomicity in global transactions involving heterogeneous components.
XA指的是eXtended Architecture,它是一种分布式事务处理的规范。XA的目标是在涉及异构组件的全局事务中提供原子性。
XA specification provides integrity through a protocol known as a two-phase commit. Two-phase commit is a widely-used distributed algorithm to facilitate the decision to commit or rollback a distributed transaction.
XA规范通过一个被称为两阶段提交的协议提供完整性。两阶段提交是一种广泛使用的分布式算法,以促进决定提交或回滚一个分布式事务。
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.
Java Transaction API(JTA)是在Java Community Process下开发的一个Java企业版API。它使Java应用程序和应用服务器能够跨XA资源执行分布式事务。
JTA is modeled around XA architecture, leveraging two-phase commit. JTA specifies standard Java interfaces between a transaction manager and the other parties in a distributed transaction.
JTA是围绕XA架构建模的,利用了两阶段提交。JTA规定了事务管理器和分布式事务中其他各方之间的标准Java接口。
3. Introduction to Atomikos
3.Atomikos简介
Now that we’ve gone through the basics of transactions, we’re ready to learn Atomikos. In this section, we’ll understand what exactly Atomikos is and how it relates to concepts like XA and JTA. We’ll also understand the architecture of Atomikos and go through its product offerings.
现在我们已经了解了事务的基本知识,我们准备学习Atomikos。在本节中,我们将了解Atomikos到底是什么,以及它与XA和JTA等概念的关系。我们还将了解Atomikos的架构,并浏览其产品。
3.1. What Is Atomikos
3.1.什么是Atomikos
As we have seen, JTA provides interfaces in Java for building applications with distributed transactions. Now, JTA is just a specification and does not offer any implementation. For us to run an application where we leverage JTA, we need an implementation of JTA. Such an implementation is called a transaction manager.
正如我们所看到的,JTA在Java中提供了用于构建具有分布式事务的应用程序的接口。现在,JTA只是一个规范,并没有提供任何实现。对于我们来说要运行一个利用JTA的应用程序,我们需要一个JTA的实现。这样的实现被称为事务管理器。
Typically, the application server provides a default implementation of the transaction manager. For instance, in the case of Enterprise Java Beans (EJB), EJB containers manage transaction behavior without any explicit intervention by application developers. However, in many cases, this may not be ideal, and we may need direct control over the transaction independent of the application server.
通常情况下,应用服务器提供一个默认的事务管理器的实现。例如,在Enterprise Java Beans(EJB)的情况下,EJB容器管理事务行为,而不需要应用开发者的任何明确干预。然而,在很多情况下,这可能并不理想,我们可能需要对独立于应用服务器的事务进行直接控制。
Atomikos is a lightweight transaction manager for Java that enables applications using distributed transactions to be self-contained. Essentially, our application doesn’t need to rely on a heavyweight component like an application server for transactions. This brings the concept of distributed transactions closer to a cloud-native architecture.
Atomikos是一个用于Java的轻量级事务管理器,它使使用分布式事务的应用程序能够自成一体。从本质上讲,我们的应用不需要依赖像应用服务器这样的重量级组件来处理事务。这使分布式事务的概念更接近于云原生架构。
3.2. Atomikos Architecture
3.2.Atomikos架构
Atomikos is built primarily as a JTA transaction manager and, hence, implements XA architecture with a two-phase commit protocol. Let’s see a high-level architecture with Atomikos:
Atomikos主要是作为一个JTA事务管理器来构建的,因此,实现了具有两阶段提交协议的XA架构。让我们看看Atomikos的高层架构。
Here, Atomikos is facilitating a two-phase-commit-based transaction spanning across a database and a message queue.
在这里,Atomikos正在促进一个跨越数据库和消息队列的基于两相commit的事务。
3.3. Atomikos Product Offerings
3.3.Atomikos提供的产品
Atomikos is a distributed transaction manager that offers more features than what JTA/XA mandates. It has an open-source product and a much more comprehensive commercial offering:
Atomikos是一个分布式事务管理器,它提供了比JTA/XA规定的更多的功能。它有一个开源的产品和一个更全面的商业产品。
- TransactionsEssentials: Atomikos’ open-source product providing JTA/XA transaction manager for Java applications working with databases and message queues. This is mostly useful for testing and evaluation purposes.
- ExtremeTransactions: the commercial offering of Atomikos, which offers distributed transactions across composite applications, including REST services apart from databases and message queues. This is useful to build applications performing Extreme Transaction Processing (XTP).
In this tutorial, we’ll use the TransactionsEssentials library to build and demonstrate the capabilities of Atomikos.
在本教程中,我们将使用TransactionsEssentials库来构建和演示Atomikos的功能。
4. Setting up Atomikos
4.设置Atomikos
As we’ve seen earlier, one of the highlights of Atomikos is that it’s an embedded transaction service. What this means is that we can run it in the same JVM as our application. Thus, setting up Atomikos is quite straightforward.
正如我们前面所看到的,Atomikos的一个亮点是它是一个嵌入式事务服务。这意味着,我们可以在与我们的应用程序相同的JVM中运行它。因此,设置Atomikos是非常简单的。
4.1. Dependencies
4.1. 依赖性
First, we need to set up the dependencies. Here, all we have to do is declare the dependencies in our Maven pom.xml file:
首先,我们需要设置依赖性。这里,我们要做的就是在Maven的pom.xml文件中声明依赖关系。
<dependency>
<groupId>com.atomikos</groupId>
<artifactId>transactions-jdbc</artifactId>
<version>5.0.6</version>
</dependency>
<dependency>
<groupId>com.atomikos</groupId>
<artifactId>transactions-jms</artifactId>
<version>5.0.6</version>
</dependency>
We’re using Atomikos dependencies for JDBC and JMS in this case, but similar dependencies are available on Maven Central for other XA-complaint resources.
在本案例中,我们使用Atomikos对JDBC和JMS的依赖,但Maven Central上也有类似的依赖,用于其他XA-complaint资源。
4.2. Configurations
4.2.配置
Atomikos provides several configuration parameters, with sensible defaults for each of them. The easiest way to override these parameters is to provide a transactions.properties file in the classpath. We can add several parameters for the initialization and operation of the transaction service. Let’s see a simple configuration to override the directory where log files are created:
Atomikos提供了几个配置参数,每个参数都有合理的默认值。覆盖这些参数的最简单方法是在classpath中提供一个transactions.properties文件。我们可以为交易服务的初始化和操作添加几个参数。让我们看看一个简单的配置来覆盖创建日志文件的目录。
com.atomikos.icatch.file=path_to_your_file
Similarly, there are other parameters that we can use to control the timeout for transactions, set unique names for our application, or define shutdown behavior.
同样,还有其他一些参数,我们可以用来控制事务的超时,为我们的应用程序设置唯一的名称,或者定义关闭行为。
4.3. Databases
4.3.数据库
In our tutorial, we’ll build a simple retail application, like the one we described earlier, which reserves inventory and then places an order. We’ll use a relational database for simplicity. Moreover, we’ll use multiple databases to demonstrate distributed transactions. However, this can very well extend to other XA-complaint resources like message queues and topics.
在我们的教程中,我们将建立一个简单的零售应用程序,就像我们前面描述的那样,储备库存,然后下订单。为了简单起见,我们将使用一个关系型数据库。此外,我们将使用多个数据库来演示分布式事务。然而,这完全可以扩展到其他XA-投诉资源,如消息队列和主题。
Our inventory database will have a simple table to host product inventories:
我们的库存数据库将有一个简单的表来承载产品库存。
CREATE TABLE INVENTORY (
productId VARCHAR PRIMARY KEY,
balance INT
);
And, our order database will have a simple table to host placed orders:
而且,我们的订单数据库将有一个简单的表来存放已下的订单。
CREATE TABLE ORDERS (
orderId VARCHAR PRIMARY KEY,
productId VARCHAR,
amount INT NOT NULL CHECK (amount <= 5)
);
This is a very basic database schema and useful only for the demonstration. However, it’s important to note that our schema constraint does not allow order with a product quantity of more than five.
这是一个非常基本的数据库模式,只对演示有用。然而,需要注意的是,我们的模式约束不允许产品数量超过5的订单。
5. Working With Atomikos
5.与Atomikos合作
Now, we’re ready to use one of the Atomikos libraries to build our application with distributed transactions. In the following subsections, we’ll use the built-in Atomikos resource adapters to connect with our back-end database systems. This is the quickest and easiest way to get started with Atomikos.
现在,我们已经准备好使用Atomikos库中的一个库来构建我们具有分布式事务的应用程序。在下面的小节中,我们将使用内置的Atomikos资源适配器来与我们的后端数据库系统连接。这是开始使用Atomikos的最快速和最简单的方法。
5.1. Instantiating UserTransaction
5.1.实例化UserTransaction
We will leverage JTA UserTransaction to demarcate transaction boundaries. All other steps related to transaction service will be automatically taken care of. This includes enlisting and delisting resources with the transaction service.
我们将利用JTA的UserTransaction来划分交易的边界。所有与交易服务相关的其他步骤将被自动处理。这包括将资源列入和从交易服务中除名。
Firstly, we need to instantiate a UserTransaction from Atomikos:
首先,我们需要从Atomikos实例化一个UserTransaction。
UserTransactionImp utx = new UserTransactionImp();
5.2. Instantiating DataSource
5.2.实例化DataSource
Then, we need to instantiate a DataSource from Atomikos. There are two versions of DataSource that Atomikos makes available.
然后,我们需要从Atomikos实例化一个DataSource。Atomikos有两个版本的DataSource可用。
The first, AtomikosDataSourceBean, is aware of an underlying XADataSource:
第一个,AtomikosDataSourceBean,知道底层的XADataSource。
AtomikosDataSourceBean dataSource = new AtomikosDataSourceBean();
While AtomikosNonXADataSourceBean uses any regular JDBC driver class:
而AtomikosNonXADataSourceBean使用任何常规JDBC驱动类。
AtomikosNonXADataSourceBean dataSource = new AtomikosNonXADataSourceBean();
As the name suggests, AtomikosNonXADataSource is not XA compliant. Hence transactions executed with such a data source can not be guaranteed to be atomic. So why would we ever use this? We may have some database that does not support XA specification. Atomikos does not prohibit us from using such a data source and still try to provide atomicity if there is a single such data source in the transaction. This technique is similar to Last Resource Gambit, a variation of the two-phase commit process.
顾名思义,AtomikosNonXADataSource不符合XA标准。因此,用这样的数据源执行的事务不能保证是原子性的。那么我们为什么要使用这个呢?我们可能有一些不支持XA规范的数据库。Atomikos并不禁止我们使用这样的数据源,如果事务中只有一个这样的数据源,仍然可以尝试提供原子性。这种技术类似于Last Resource Gambit,是两阶段提交过程的一个变种。
Further, we need to appropriately configure the DataSource depending upon the database and driver.
此外,我们需要根据数据库和驱动适当地配置DataSource。
5.3. Performing Database Operations
5.3.执行数据库操作
Once configured, it’s fairly easy to use DataSource within the context of a transaction in our application:
一旦配置好,在我们的应用程序中的事务背景下使用DataSource就相当容易。
public void placeOrder(String productId, int amount) throws Exception {
String orderId = UUID.randomUUID().toString();
boolean rollback = false;
try {
utx.begin();
Connection inventoryConnection = inventoryDataSource.getConnection();
Connection orderConnection = orderDataSource.getConnection();
Statement s1 = inventoryConnection.createStatement();
String q1 = "update Inventory set balance = balance - " + amount + " where productId ='" +
productId + "'";
s1.executeUpdate(q1);
s1.close();
Statement s2 = orderConnection.createStatement();
String q2 = "insert into Orders values ( '" + orderId + "', '" + productId + "', " + amount + " )";
s2.executeUpdate(q2);
s2.close();
inventoryConnection.close();
orderConnection.close();
} catch (Exception e) {
rollback = true;
} finally {
if (!rollback)
utx.commit();
else
utx.rollback();
}
}
Here, we are updating the database tables for inventory and order within the transaction boundary. This automatically provides the benefit of these operations happening atomically.
在这里,我们在事务边界内更新库存和订单的数据库表。这自动提供了这些操作原子化的好处。
5.4. Testing Transactional Behavior
5.4.测试事务性行为
Finally, we must be able to test our application with simple unit tests to validate that the transaction behavior is as expected:
最后,我们必须能够用简单的单元测试来测试我们的应用程序,以验证交易行为是否符合预期。
@Test
public void testPlaceOrderSuccess() throws Exception {
int amount = 1;
long initialBalance = getBalance(inventoryDataSource, productId);
Application application = new Application(inventoryDataSource, orderDataSource);
application.placeOrder(productId, amount);
long finalBalance = getBalance(inventoryDataSource, productId);
assertEquals(initialBalance - amount, finalBalance);
}
@Test
public void testPlaceOrderFailure() throws Exception {
int amount = 10;
long initialBalance = getBalance(inventoryDataSource, productId);
Application application = new Application(inventoryDataSource, orderDataSource);
application.placeOrder(productId, amount);
long finalBalance = getBalance(inventoryDataSource, productId);
assertEquals(initialBalance, finalBalance);
}
Here, we’re expecting a valid order to decrease the inventory, while we’re expecting an invalid order to leave the inventory unchanged. Please note that, as per our database constraint, any order with a quantity of more than five of a product is considered an invalid order.
在这里,我们期望一个有效的订单能减少库存,而我们期望一个无效的订单能让库存保持不变。请注意,根据我们的数据库约束,任何数量超过5个产品的订单都被视为无效订单。
5.5. Advanced Atomikos Usage
5.5.高级Atomikos用法
The example above is the simplest way to use Atomikos and perhaps sufficient for most of the requirements. However, there are other ways in which we can use Atomikos to build our application. While some of these options make Atomikos easy to use, others offer more flexibility. The choice depends on our requirements.
上面的例子是使用Atomikos的最简单方法,也许足以满足大多数要求。然而,还有其他的方式,我们可以使用Atomikos来构建我们的应用程序。虽然其中一些选项使Atomikos易于使用,但其他选项提供了更多的灵活性。选择取决于我们的要求。
Of course, it’s not necessary to always use Atomikos adapters for JDBC/JMS. We can choose to use the Atomikos transaction manager while working directly with XAResource. However, in that case, we have to explicitly take care of enlisting and delisting XAResource instances with the transaction service.
当然,没有必要总是使用Atomikos适配器来处理JDBC/JMS。我们可以选择使用Atomikos事务管理器,同时直接使用XAResource。然而,在这种情况下,我们必须明确地注意将XAResource实例与事务服务一起列入名单和取消名单。
Atomikos also makes it possible to use more advanced features through a proprietary interface, UserTransactionService. Using this interface, we can explicitly register resources for recovery. This gives us fine-grained control over what resources should be recovered, how they should be recovered, and when recovery should happen.
Atomikos还可以通过一个专有接口UserTransactionService来使用更高级的功能。使用这个接口,我们可以明确地注册资源进行恢复。这使我们能够精细地控制哪些资源应该被恢复,如何恢复,以及何时恢复。
6. Integrating Atomikos
6.整合Atomikos
While Atomikos provides excellent support for distributed transactions, it’s not always convenient to work with such low-level APIs. To focus on the business domain and avoid the clutter of boilerplate code, we often need the support of different frameworks and libraries. Atomikos supports most of the popular Java frameworks related to back-end integrations. We’ll explore a couple of them here.
虽然Atomikos为分布式事务提供了很好的支持,但使用这种低级别的API并不总是很方便。为了专注于业务领域,避免杂乱无章的模板代码,我们常常需要不同框架和库的支持。Atomikos支持大多数与后端集成相关的流行Java框架。我们将在此探讨其中的几个。
6.1. Atomikos With Spring and DataSource
6.1.Atomikos与Spring和DataSource
Spring is one of the popular frameworks in Java that provides an Inversion of Control (IoC) container. Notably, it has fantastic support for transactions as well. It offers declarative transaction management using Aspect-Oriented Programming (AOP) techniques.
Spring是Java中流行的框架之一,它提供了一个反转控制(IoC)容器。值得注意的是,它对事务也有很好的支持。它使用面向方面的编程(AOP)技术提供声明式的事务管理。
Spring supports several transaction APIs, including JTA for distributed transactions. We can use Atomikos as our JTA transaction manager within Spring without much effort. Most importantly, our application remains pretty much agnostic to Atomikos, thanks to Spring.
Spring支持多个事务API,包括用于分布式事务的JTA。我们可以在Spring中使用Atomikos作为我们的JTA事务管理器,而不需要太多努力。最重要的是,由于Spring的存在,我们的应用程序对Atomikos几乎没有任何影响。
Let’s see how we can solve our previous problem, this time leveraging Spring. We’ll begin by rewriting the Application class:
让我们看看如何解决我们之前的问题,这次是利用Spring。我们将从重写Application类开始。
public class Application {
private DataSource inventoryDataSource;
private DataSource orderDataSource;
public Application(DataSource inventoryDataSource, DataSource orderDataSource) {
this.inventoryDataSource = inventoryDataSource;
this.orderDataSource = orderDataSource;
}
@Transactional(rollbackFor = Exception.class)
public void placeOrder(String productId, int amount) throws Exception {
String orderId = UUID.randomUUID().toString();
Connection inventoryConnection = inventoryDataSource.getConnection();
Connection orderConnection = orderDataSource.getConnection();
Statement s1 = inventoryConnection.createStatement();
String q1 = "update Inventory set balance = balance - " + amount + " where productId ='" +
productId + "'";
s1.executeUpdate(q1);
s1.close();
Statement s2 = orderConnection.createStatement();
String q2 = "insert into Orders values ( '" + orderId + "', '" + productId + "', " + amount + " )";
s2.executeUpdate(q2);
s2.close();
inventoryConnection.close();
orderConnection.close();
}
}
As we can see here, most of the transaction-related boilerplate code has been replaced by a single annotation at the method level. Moreover, Spring takes care of instantiating and injecting DataSource, which our application depends on.
正如我们在这里看到的,大部分与交易相关的模板代码已经被方法级别的一个注释所取代。此外,Spring还负责实例化和注入我们的应用程序所依赖的DataSource,。
Of course, we have to provide relevant configurations to Spring. We can use a simple Java class to configure these elements:
当然,我们必须向Spring提供相关的配置。我们可以使用一个简单的Java类来配置这些元素。
@Configuration
@EnableTransactionManagement
public class Config {
@Bean(initMethod = "init", destroyMethod = "close")
public AtomikosDataSourceBean inventoryDataSource() {
AtomikosDataSourceBean dataSource = new AtomikosDataSourceBean();
// Configure database holding order data
return dataSource;
}
@Bean(initMethod = "init", destroyMethod = "close")
public AtomikosDataSourceBean orderDataSource() {
AtomikosDataSourceBean dataSource = new AtomikosDataSourceBean();
// Configure database holding order data
return dataSource;
}
@Bean(initMethod = "init", destroyMethod = "close")
public UserTransactionManager userTransactionManager() throws SystemException {
UserTransactionManager userTransactionManager = new UserTransactionManager();
userTransactionManager.setTransactionTimeout(300);
userTransactionManager.setForceShutdown(true);
return userTransactionManager;
}
@Bean
public JtaTransactionManager jtaTransactionManager() throws SystemException {
JtaTransactionManager jtaTransactionManager = new JtaTransactionManager();
jtaTransactionManager.setTransactionManager(userTransactionManager());
jtaTransactionManager.setUserTransaction(userTransactionManager());
return jtaTransactionManager;
}
@Bean
public Application application() {
return new Application(inventoryDataSource(), orderDataSource());
}
}
Here, we are configuring AtomikosDataSourceBean for the two different databases holding our inventory and order data. Moreover, we’re also providing the necessary configuration for the JTA transaction manager.
在这里,我们正在为存放库存和订单数据的两个不同的数据库配置AtomikosDataSourceBean。此外,我们还为JTA事务管理器提供了必要的配置。
Now, we can test our application for transactional behavior as before. Again, we should be validating that a valid order reduces our inventory balance, while an invalid order leaves it unchanged.
现在,我们可以像以前一样测试我们的应用程序的交易行为。同样,我们应该验证一个有效的订单会减少我们的库存余额,而一个无效的订单会让它保持不变。
6.2. Atomikos With Spring, JPA, and Hibernate
6.2.使用Spring、JPA和Hibernate的Atomikos
While Spring has helped us cut down boilerplate code to a certain extent, it’s still quite verbose. Some tools can make working with relational databases in Java even easier. Java Persistence API (JPA) is a specification that describes the management of relational data in Java applications. This simplifies the data access and manipulation code to a large extent.
虽然Spring在一定程度上帮助我们减少了模板代码,但它仍然相当冗长。一些工具可以使在Java中使用关系型数据库的工作更加容易。Java Persistence API(JPA)是一个描述Java应用程序中关系数据管理的规范。这在很大程度上简化了数据访问和操作的代码。
Hibernate is one of the most popular implementations of the JPA specification. Atomikos has great support for several JPA implementations, including Hibernate. As before, our application remains agnostic to Atomikos as well as Hibernate, thanks to Spring and JPA!
Hibernate是JPA规范中最流行的实现之一。Atomikos对几种JPA实现有很大的支持,包括Hibernate。和以前一样,由于Spring和JPA的存在,我们的应用程序对Atomikos和Hibernate都保持着不可知性。
Let’s see how Spring, JPA, and Hibernate can make our application even more concise while providing the benefits of distributed transactions through Atomikos. As before, we will begin by rewriting the Application class:
让我们看看Spring、JPA和Hibernate如何使我们的应用程序更加简洁,同时通过Atomikos提供分布式事务的好处。和以前一样,我们将从重写Application类开始。
public class Application {
@Autowired
private InventoryRepository inventoryRepository;
@Autowired
private OrderRepository orderRepository;
@Transactional(rollbackFor = Exception.class)
public void placeOrder(String productId, int amount) throws SQLException {
String orderId = UUID.randomUUID().toString();
Inventory inventory = inventoryRepository.findOne(productId);
inventory.setBalance(inventory.getBalance() - amount);
inventoryRepository.save(inventory);
Order order = new Order();
order.setOrderId(orderId);
order.setProductId(productId);
order.setAmount(new Long(amount));
orderRepository.save(order);
}
}
As we can see, we’re not dealing with any low-level database APIs now. However, for this magic to work, we do need to configure Spring Data JPA classes and configurations. We’ll begin by defining our domain entities:
正如我们所看到的,我们现在没有处理任何低级别的数据库API。然而,为了使这个魔法发挥作用,我们确实需要配置Spring Data JPA类和配置。我们将从定义我们的领域实体开始。
@Entity
@Table(name = "INVENTORY")
public class Inventory {
@Id
private String productId;
private Long balance;
// Getters and Setters
}
@Entity
@Table(name = "ORDERS")
public class Order {
@Id
private String orderId;
private String productId;
@Max(5)
private Long amount;
// Getters and Setters
}
Next, we need to provide the repositories for these entities:
接下来,我们需要为这些实体提供存储库。
@Repository
public interface InventoryRepository extends JpaRepository<Inventory, String> {
}
@Repository
public interface OrderRepository extends JpaRepository<Order, String> {
}
These are quite simple interfaces, and Spring Data takes care of elaborating these with actual code to work with database entities.
这些都是非常简单的接口,Spring Data负责用实际的代码来阐述这些接口,以便与数据库实体一起工作。
Finally, we need to provide the relevant configurations for a data source for both inventory and order databases and the transaction manager:
最后,我们需要为库存和订单数据库的数据源以及交易管理器提供相关配置。
@Configuration
@EnableJpaRepositories(basePackages = "com.baeldung.atomikos.spring.jpa.inventory",
entityManagerFactoryRef = "inventoryEntityManager", transactionManagerRef = "transactionManager")
public class InventoryConfig {
@Bean(initMethod = "init", destroyMethod = "close")
public AtomikosDataSourceBean inventoryDataSource() {
AtomikosDataSourceBean dataSource = new AtomikosDataSourceBean();
// Configure the data source
return dataSource;
}
@Bean
public EntityManagerFactory inventoryEntityManager() {
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter(vendorAdapter);
// Configure the entity manager factory
return factory.getObject();
}
}
@Configuration
@EnableJpaRepositories(basePackages = "com.baeldung.atomikos.spring.jpa.order",
entityManagerFactoryRef = "orderEntityManager", transactionManagerRef = "transactionManager")
public class OrderConfig {
@Bean(initMethod = "init", destroyMethod = "close")
public AtomikosDataSourceBean orderDataSource() {
AtomikosDataSourceBean dataSource = new AtomikosDataSourceBean();
// Configure the data source
return dataSource;
}
@Bean
public EntityManagerFactory orderEntityManager() {
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter(vendorAdapter);
// Configure the entity manager factory
return factory.getObject();
}
}
@Configuration
@EnableTransactionManagement
public class Config {
@Bean(initMethod = "init", destroyMethod = "close")
public UserTransactionManager userTransactionManager() throws SystemException {
UserTransactionManager userTransactionManager = new UserTransactionManager();
userTransactionManager.setTransactionTimeout(300);
userTransactionManager.setForceShutdown(true);
return userTransactionManager;
}
@Bean
public JtaTransactionManager transactionManager() throws SystemException {
JtaTransactionManager jtaTransactionManager = new JtaTransactionManager();
jtaTransactionManager.setTransactionManager(userTransactionManager());
jtaTransactionManager.setUserTransaction(userTransactionManager());
return jtaTransactionManager;
}
@Bean
public Application application() {
return new Application();
}
}
This is still quite a lot of configuration that we have to do. This is partly because we’re configuring Spring JPA for two separate databases. Also, we can further reduce these configurations through Spring Boot, but that’s beyond the scope of this tutorial.
这仍然是我们必须要做的相当多的配置。这部分是因为我们要为两个独立的数据库配置Spring JPA。另外,我们还可以通过Spring Boot进一步减少这些配置,但这已经超出了本教程的范围。
As before, we can test our application for the same transactional behavior. There’s nothing new this time, except for the fact that we’re using Spring Data JPA with Hibernate now.
和以前一样,我们可以测试我们的应用程序是否有相同的事务性行为。这次没有什么新东西,只是我们现在使用的是Spring Data JPA和Hibernate。
7. Atomikos Beyond JTA
7.Atomikos超越JTA
While JTA provides excellent transaction support for distributed systems, these systems must be XA-complaint like most relational databases or message queues. However, JTA is not useful if one of these systems doesn’t support XA specification for a two-phase commit protocol. Several resources fall under this category, especially within a microservices architecture.
虽然JTA为分布式系统提供了出色的事务支持,但这些系统必须像大多数关系型数据库或消息队列一样是XA规范的。然而,如果这些系统中有一个不支持XA规范的两阶段提交协议,那么JTA就没有用。有几个资源属于这个类别,特别是在微服务架构中。
Several alternative protocols support distributed transactions. One of these is a variation of two-phase commit protocol that makes use of compensations. Such transactions have a relaxed isolation guarantee and are known as compensation-based transactions. Participants commit the individual parts of the transaction in the first phase itself, offering a compensation handler for a possible rollback in the second phase.
有几个替代协议支持分布式事务。其中之一是利用补偿的两阶段提交协议的变体。这种交易具有宽松的隔离保证,被称为基于补偿的交易。参与者在第一阶段本身提交交易的各个部分,在第二阶段为可能的回滚提供一个补偿处理程序。
There are several design patterns and algorithms to implement a compensation-based transaction. For example, Sagas is one such popular design pattern. However, they are usually complex to implement and error-prone.
有几种设计模式和算法来实现基于补偿的交易。例如,Sagas就是这样一种流行的设计模式。然而,它们通常实现起来很复杂,而且容易出错。
Atomikos offers a variation of compensation-based transaction called Try-Confirm/Cancel (TCC). TCC offers better business semantics to the entities under a transaction. However, this is possible only with advanced architecture support from the participants, and TCC is only available under the Atomikos commercial offering, ExtremeTransactions.
Atomikos提供了一种基于补偿的交易的变体,称为 “尝试-确认/取消”(TCC)。TCC为交易下的实体提供了更好的业务语义。然而,这只有在参与者的高级架构支持下才有可能,而且TCC只在Atomikos的商业产品ExtremeTransactions下可用。
8. Alternatives to Atomikos
8.替代Atomikos的产品
We have gone through enough of Atomikos to appreciate what it has to offer. Moreover, there’s a commercial offering from Atomikos with even more powerful features. However, Atomikos is not the only option when it comes to choosing a JTA transaction manager. There are a few other credible options to choose from. Let’s see how they fare against Atomikos.
我们已经浏览了足够多的Atomikos,欣赏它所提供的东西。此外,Atomikos还有一个商业产品,其功能甚至更加强大。然而,在选择JTA事务管理器时,Atomikos不是唯一的选择。还有其他一些可靠的选项可供选择。让我们看看它们与Atomikos相比如何。
8.1. Narayana
8.1.纳拉亚纳
Narayana is perhaps one of the oldest open-source distributed transaction managers and is currently managed by Red Hat. It has been widely used across the industry, and it has evolved through community support and influenced several specifications and standards.
Narayana是可能是最古老的开源分布式事务管理器之一,目前由Red Hat管理。它在整个行业中得到了广泛的应用,它通过社区的支持不断发展,并影响了一些规范和标准。
Narayana provides support for a wide range of transaction protocols like JTA, JTS, Web-Services, and REST, to name a few. Further, Narayana can be embedded in a wide range of containers.
Narayana提供对广泛的交易协议的支持,如JTA、JTS、Web-Services和REST等等。此外,Narayana可以被嵌入到各种容器中。
Compared to Atomikos, Narayana provides pretty much all the features of a distributed transaction manager. In many cases, Narayana is more flexible to integrate and use in applications. For instance, Narayana has language bindings for both C/C++ and Java. However, this comes at the cost of added complexity, and Atomikos is comparatively easier to configure and use.
与Atomikos相比,Narayana提供了几乎所有分布式事务管理器的功能。在许多情况下,Narayana在应用中的集成和使用更加灵活。例如,Narayana有C/C++和Java的语言绑定。然而,这是以增加复杂性为代价的,而Atomikos相对来说更容易配置和使用。
8.2. Bitronix
8.2 Bitronix
Bitronix is a fully working XA transaction manager that provides all services required by the JTA API. Importantly, Bitronix is an embeddable transaction library that provides extensive and useful error reporting and logging. For a distributed transaction, this makes it easier to investigate failures. Moreover, it has excellent support for Spring’s transactional capabilities and works with minimal configurations.
Bitronix是一个完全工作的XA事务管理器,它提供JTA API所要求的所有服务。重要的是,Bitronix是一个可嵌入的事务库,它提供广泛而有用的错误报告和日志。对于一个分布式事务来说,这使得调查故障变得更加容易。此外,它对Spring的事务性功能有很好的支持,并以最小的配置工作。
Compared to Atomikos, Bitronix is an open-source project and does not have a commercial offering with product support. The key features that are part of Atomikos’ commercial offering but are lacking in Bitronix include support for microservices and declarative elastic scaling capability.
与Atomikos相比,Bitronix是一个开源项目,没有产品支持的商业产品。作为Atomikos商业产品的一部分,Bitronix缺乏的关键功能包括对微服务的支持和声明式弹性扩展能力。
9. Conclusion
9.结语
To sum up, in this tutorial, we went through the basic details of transactions. We understood what distributed transactions are and how a library like Atomikos can facilitate in performing them. In the process, we leveraged the Atomikos APIs to create a simple application with distributed transactions.
总而言之,在本教程中,我们了解了事务的基本细节。我们了解了什么是分布式事务,以及像Atomikos这样的库是如何促进执行这些事务的。在这个过程中,我们利用Atomikos APIs创建了一个带有分布式事务的简单应用。
We also understood how Atomikos works with other popular Java frameworks and libraries. Finally, we went through some of the alternatives to Atomikos that are available to us.
我们还了解了Atomikos如何与其他流行的Java框架和库一起工作。最后,我们了解了一些可以替代Atomikos的方法。
As usual, the source code for this article can be found over on GitHub.
像往常一样,本文的源代码可以在GitHub上找到超过。