javascript
Distributed transactions with multiple databases, Spring Boot, Spring Data JPA and Atomikos
2019獨角獸企業重金招聘Python工程師標準>>>
A couple of weeks ago I was evaluating the possibility to use Spring Boot, Spring Data JPA and Atomikos for distributed transactions involving multiple databases. After looking at the Spring blog article (which involves one database and ActiveMQ) and having done some attempts, I could not get it to work with two databases. The configuration seemed fine, but the Entity Manager did not get notified when persisting my entities. So I wrote this question on StackOverflow, which has been answered directly by Dave Syer and Oliver Gierke. This post is to share and discuss the solution.
Description of the case and entities model
We want to be able to save two entities at the same time into two different databases; the operation must be transactional. So, in this example, we have a Customer entity, which is persisted in the first database, and an Order entity which is persisted in the second database. The two entities are very simple, as they serve only as a demonstration.
The resulting implementation is the following. It's worth noting that they belong to two different packages, for two main reasons:
Write repositories interfaces
Also in this case it's standard, the only thing to notice is that I put the two interfaces in two different packages. The reason is explained in the next step.
package com.at.mul.repository.customer;import org.springframework.data.jpa.repository.JpaRepository;import com.at.mul.domain.customer.Customer;public interface CustomerRepository extends JpaRepository<Customer, Integer> {} package com.at.mul.repository.order;import org.springframework.data.jpa.repository.JpaRepository;import com.at.mul.domain.order.Order;public interface OrderRepository extends JpaRepository<Order, Integer> {}Write configuration classes
This is where it becomes interesting. The @DependsOn("transactionManager") annotation is not mandatory, but I needed this to get rid of several warnings at tests (or application) startup, like WARNING: transaction manager not running? in the logs. The next annotation @EnableJpaRepositories is more important:- it specifies which are the packages to scan for annotated components (repository interfaces), and in my case I wanted only repositories related to the customer (and conversely to the order).
- it specifies which is the entity manager to be used to manage entities, in my case the customerEntityManager for customer related operations and orderEntityManager for order related operations
- it specifies the transaction manager to be used, in my case the transactionManager defined in the MainConfig class. This needs to be the same for every @EnableJpaRepositories to get distributed transactions working
Another important thing here is the definition of the LocalContainerEntityManagerFactoryBean.
- the @Bean annotation has a given name, that is the one specified in the @EnableJpaRepositories annotation.
- you need to set some properties to the JpaPropertyMap, in particular you need to say that the transaction type is JTA and that the jta platform is AtomikosJtaPlatform.class.getName()
Write the AbstractJtaPlatform implementation
As said, this is the most important step, as we need to write the implementation of that class by ourselves since Hibernate does not provide it. Here is the resulting code:
package com.at.mul;import javax.transaction.TransactionManager; import javax.transaction.UserTransaction;import org.hibernate.engine.transaction.jta.platform.internal.AbstractJtaPlatform;public class AtomikosJtaPlatform extends AbstractJtaPlatform {private static final long serialVersionUID = 1L;static TransactionManager transactionManager;static UserTransaction transaction;@Overrideprotected TransactionManager locateTransactionManager() {return transactionManager;}@Overrideprotected UserTransaction locateUserTransaction() {return transaction;}}Write the main configuration class
Also in this case it's a pretty standard class, with @EnableTransactionManagement annotation and Atomikos bean definitions. The only very important thing to notice is that we need to set AtomikosJtaPlatform.transactionManager and AtomikosJtaPlatform.transaction attributes.
package com.at.mul;import javax.transaction.TransactionManager; import javax.transaction.UserTransaction;import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.DependsOn; import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; import org.springframework.orm.jpa.JpaVendorAdapter; import org.springframework.orm.jpa.vendor.Database; import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.annotation.EnableTransactionManagement; import org.springframework.transaction.jta.JtaTransactionManager;import com.atomikos.icatch.jta.UserTransactionImp; import com.atomikos.icatch.jta.UserTransactionManager;@Configuration @ComponentScan @EnableTransactionManagement public class MainConfig {@Beanpublic PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {return new PropertySourcesPlaceholderConfigurer();}@Beanpublic JpaVendorAdapter jpaVendorAdapter() {HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();hibernateJpaVendorAdapter.setShowSql(true);hibernateJpaVendorAdapter.setGenerateDdl(true);hibernateJpaVendorAdapter.setDatabase(Database.H2);return hibernateJpaVendorAdapter;}@Bean(name = "userTransaction")public UserTransaction userTransaction() throws Throwable {UserTransactionImp userTransactionImp = new UserTransactionImp();userTransactionImp.setTransactionTimeout(10000);return userTransactionImp;}@Bean(name = "atomikosTransactionManager", initMethod = "init", destroyMethod = "close")public TransactionManager atomikosTransactionManager() throws Throwable {UserTransactionManager userTransactionManager = new UserTransactionManager();userTransactionManager.setForceShutdown(false);AtomikosJtaPlatform.transactionManager = userTransactionManager;return userTransactionManager;}@Bean(name = "transactionManager")@DependsOn({ "userTransaction", "atomikosTransactionManager" })public PlatformTransactionManager transactionManager() throws Throwable {UserTransaction userTransaction = userTransaction();AtomikosJtaPlatform.transaction = userTransaction;TransactionManager atomikosTransactionManager = atomikosTransactionManager();return new JtaTransactionManager(userTransaction, atomikosTransactionManager);}}Resources
Here is the resulting structure of the project:
You can see the full source code here: https://github.com/fabiomaffioletti/mul-at, The master branch uses in memory database. Checkout branch named mysql-db to use real databases (see application.properties to tweak your database connection data).
轉載于:https://my.oschina.net/pangzhuzhu/blog/318126
總結
以上是生活随笔為你收集整理的Distributed transactions with multiple databases, Spring Boot, Spring Data JPA and Atomikos的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: elman神经网络的实现
- 下一篇: nagios-3.4.3搭建