Spring JPAで複数データベース(PostgreSQL)に接続する方法

Spring JPAで複数データベース(PostgreSQL)に接続する方法

JPAで複数データベースに接続するけど、トランザクション管理は別々にしたいです。例えば、MyDB1を更新して、そのあとにMyDB2更新時に失敗した場合に、MyDB1をロールバックせずにコミットしておきたい。

今回は2つのデータベースともPostgreSQL9.6を使用しています。

複数データベースに接続する

1つ目のDB接続情報をprimary、もう一つのDB接続情報をsecondaryとします。

application.ymlには以下のように記述します。

spring:
jpa:
database: POSTGRESQL
datasource:
primary:
driver-class-name: org.postgresql.Driver
url: jdbc:postgresql://localhost:5432/mydb1 #mydb1というデータベース
username: postgres
password: xxxxxxxx
secondary:
driver-class-name: org.postgresql.Driver
url: jdbc:postgresql://localhost:5432/mydb2 #mydb2というデータベース
username: postgres
password: yyyyyyyy

view raw
gistfile1.txt
hosted with ❤ by GitHub

接続情報に対するクラスを定義します。接続情報を一つのクラスとして定義する為、2つのクラスを作成しておきます。

PrimaryConfig.java

package jp.co.confrage.config;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
@Configuration
@EnableJpaRepositories(
basePackages = "jp.co.confrage.repository.primary",
entityManagerFactoryRef = "primaryEntityManager",
transactionManagerRef = "primaryTransactionManager"
)
public class PrimaryConfig {
@Bean
@Primary
@ConfigurationProperties(prefix = "spring.datasource.primary")
public DataSourceProperties primaryProperties() {
return new DataSourceProperties();
}
@Bean
@Primary
@Autowired
public DataSource primaryDataSource(@Qualifier("primaryProperties")
DataSourceProperties properties) {
return properties.initializeDataSourceBuilder().build();
}
@Bean
@Primary
@Autowired
public LocalContainerEntityManagerFactoryBean primaryEntityManager(EntityManagerFactoryBuilder builder,@Qualifier("primaryDataSource") DataSource dataSource){
return builder.dataSource(dataSource)
.packages("jp.co.confrage.entity.primary")
.persistenceUnit("primary")
.build();
}
@Bean
@Primary
@Autowired
@Autowired
public JpaTransactionManager primaryTransactionManager(@Qualifier("primaryEntityManager") LocalContainerEntityManagerFactoryBean primaryEntityManager) {
@Autowired
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(primaryEntityManager.getObject());
return transactionManager;
}
}

view raw
gistfile1.txt
hosted with ❤ by GitHub

SecondaryConfig.java

package jp.co.confrage.config;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
@Configuration
@EnableJpaRepositories(
basePackages = "jp.co.confrage.repository.secondary",
entityManagerFactoryRef = "secondaryEntityManager",
transactionManagerRef = "secondaryTransactionManager"
)
public class SecondaryConfig {
@Bean
@ConfigurationProperties(prefix = "spring.datasource.secondary")
public DataSourceProperties secondaryProperties() {
return new DataSourceProperties();
}
@Bean
@Autowired
public DataSource secondaryDataSource(@Qualifier("secondaryProperties") DataSourceProperties properties) {
return properties.initializeDataSourceBuilder().build();
}
@Bean
@Autowired
public LocalContainerEntityManagerFactoryBean secondaryEntityManager(EntityManagerFactoryBuilder builder,@Qualifier("secondaryDataSource") DataSource dataSource){
return builder.dataSource(dataSource)
.packages("jp.co.confrage.entity.secondary")
.persistenceUnit("secondary")
.build();
}
@Bean
@Autowired
public JpaTransactionManager secondaryTransactionManager(@Qualifier("secondaryEntityManager") LocalContainerEntityManagerFactoryBean secondaryEntityManager) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(secondaryEntityManager.getObject());
return transactionManager;
}
}

view raw
gistfile1.txt
hosted with ❤ by GitHub

これで2つのデータベースに接続することが確認できます。トランザクション管理は別になります。

サービスレベルで@Transactionalをつけると、「Executing an update/delete query; nested exception is javax.persistence.TransactionRequiredException: Executing an update/delete query」とエラーが出ますが、リポジトリレベルで@Transactionalとすると別トランザクション管理ができます。

DataSourceProperties

@ConfigurationProperties(prefix = “spring.datasource.primary”)アノテーション、@ConfigurationProperties(prefix = “spring.datasource.secondary”)アノテーションをメソッドに付与してDataSourcePropertiesを生成します。(prefixはapplication.ymlに合わせてください)

DataSource

DataSourcePropertiesインスタンスからDataSourceを生成します。

LocalContainerEntityManagerFactoryBean

EntityManagerを生成するクラスです。Spring Bootが提供しているEntityManagerFactoryBuilderクラスからインスタンスを生成します。

packagesメソッドでEntityのパッケージを指定します。

persistenceUnitメソッドでEntityManagerのunitNameを指定します。

JpaTransactionManager

EntityManagerに対してTransactionManagerを生成します。

2つのデータベースでトランザクション管理する2相コミット(2フェーズコミット)

データベース接続は確認できましたが、複数データベースでトランザクション管理ができるかというと、@Transactionalでは実現できず、以下ライブラリを使わないとだめなようです。

bitronixは情報量が少ないように思いますが、現在も開発されています。

Atomikosも現在も開発されています。ライブラリは他にもあるようです。Spring boot公式ではこの2つをサポートしているとのことです。

https://spring.pleiades.io/spring-boot/docs/current/reference/html/spring-boot-features.html

PostgreSQL9.6で2フェーズコミットをAtomikosかbitronixを使って試してみたいと思います。

basePackagesに複数指定する

複数リポジトリを@EnableJpaRepositoriesアノテーションのbasePackages属性に{}で囲って、カンマ区切りで複数指定ができます。

スポンサーリンク
  • このエントリーをはてなブックマークに追加
  • Evernoteに保存Evernoteに保存
スポンサーリンク

コメントをどうぞ

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

CAPTCHA