Spring Bootを使用しているとデータベースへの接続も簡単にできますよね!
ただ簡単にできるからこそ、ちょっと変わったことをやろうとすると意外と苦労することがありますね。
今回はSpring BootでJPAを使用して複数データベースに接続する方法について書きます。
概要
- Spring Initializrでモジュールの導入
- Entitiyの定義
- Repositoryの定義
- Configurationの定義
- Flywayの追加設定(補足編)
1. Spring Initializrでモジュールの導入
まずはSpring Initializrを使用してモジュールを作成します。
最近はIDEでプロジェクトを新規作成するときにSpring Initializrを選択できるようになっています。(私が使用しているIntelliJは対応済み)
導入したモジュールは以下。
データベースは環境に合わせて適切なものを入れてください。
- JPA
- H2 -> 複数データベース用
- HSQLDB -> 複数データベース用
- DevTools
- Lombok
- Web -> H2 console画面を触れるように。通常不要。
- Flyway -> 補足編で使用するため。通常は不要。
2. Entitiyの定義
それではJPAを定義していきましょう。まずはEntityです。
ポイントはデータベースごとにパッケージを分けることです。
パッケージに分けることで、Configurationの設定をシンプルにできます。
(逆にパッケージ以外は特筆すべきことはないので、解説は省略します。)
これからメインのDBをprimary、サブのDBをsecondaryと呼び、prefixやパッケージはそれで分けていきます。
primary用
package aha.oretama.jp.entity.primary;
import ...
@Entity
@Data
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
}
secondary用
package aha.oretama.jp.entity.secondary;
import ...
@Entity
@Data
public class Book {
@Id
private String isbn;
private String title;
}
3. Repositoryの定義
Repositoryのポイントも同じくデータベースごとにパッケージを分けることです。
primary用
package aha.oretama.jp.repository.primary;
import ...;
public interface UserRepository extends JpaRepository<User,Integer>{
}
secondary用
package aha.oretama.jp.repository.secondary;
import ...
public interface BookRepository extends JpaRepository<Book,String> {
}
4. Configurationの定義
解説すべきことは多くあるので、まずはソースをご覧ください。
primary用
@Configuration
@EnableJpaRepositories(
basePackages = "aha.oretama.jp.repository.primary",
entityManagerFactoryRef = "primaryEntityManager",
transactionManagerRef = "primaryTransactionManager"
)
public class PrimaryDataSourceConfiguration {
@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("aha.oretama.jp.entity.primary")
.persistenceUnit("primary")
.build();
}
@Bean
@Primary
@Autowired
public JpaTransactionManager primaryTransactionManager(@Qualifier("primaryEntityManager") LocalContainerEntityManagerFactoryBean primaryEntityManager) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(primaryEntityManager.getObject());
return transactionManager;
}
}
secondary用
@Configuration
@EnableJpaRepositories(
basePackages = "aha.oretama.jp.repository.secondary",
entityManagerFactoryRef = "secondaryEntityManager",
transactionManagerRef = "secondaryTransactionManager"
)
public class SecondaryDataSourceConfiguration {
@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("aha.oretama.jp.entity.secondary")
.persistenceUnit("secondary")
.build();
}
@Bean
@Autowired
public JpaTransactionManager secondaryTransactionManager(@Qualifier("secondaryEntityManager") LocalContainerEntityManagerFactoryBean secondaryEntityManager) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(secondaryEntityManager.getObject());
return transactionManager;
}
}
以下、解説です。
DataSourcePropertiesの設定
DataSourcePropertiesはデフォルトでBean登録されており、デフォルトのDataSourcePropertiesを使用しないようにするために、必ずprimaryDB側に@primary
を付与する必要があります。
@primary
がないと、複数のBeanが登録されているためにエラーとなってしまいます。
今回はspring.datasource.primary.*
,spring.datasource.secondary.*
を読み込むように@ConfigurationProperties
を付与しています。
DataSourceの設定
それぞれのDBに対するDataSourceを定義します。
同じくprimaryDB側に@primary
をつけないとエラーになります。
@Autowired
をつけて、引数に渡したDataSourcePropertiesをインジェクションしています。
対象クラスのBeanが複数あるので@Qualifier
で明示しています。
LocalContainerEntityManagerFactoryBeanの設定
EntityManagerを生成するためのクラスになります。
EntityManagerFactoryBuilderはSpring Bootが提供しているBeanをそのままインジェクションします。
.packages(...)
で対象となるEntityが格納されたパッケージを指定することで複数データベースに対応しています。この引数はクラスの配列も受け取れるので、パッケージを分けずに、Entityのクラスを直接指定することもできます。
.persistenceUnit(...)
は永続性ユニットの名前で、複数データベースを定義するときは永続性ユニットを区別するために必須になります。
JpaTransactionManagerの設定
EntityManagerが2つあるため、それぞれのTransactionManagerを定義します。
JpaTransactionManagerを生成して、EntityManagerFactoryを設定すればOKです。
クラスの設定
対象のJpaRepositoryパッケージ、EntityManagerFactory,TransactionManagerがわかるように、@EnableJpaRepositories
で指定します。
yml or propertiesの設定
忘れないように、DataSourcePropertiesの設定で指定したプロパティ名に接続情報を記載します。
これですべての設定が完了しました!
これで複数データベースに接続できます。
5. Flywayの追加設定(補足編)
これは完全に補足です。
複数データベースを定義して、primary以外のデータベースにFlywayでマイグレーションする場合は、対象のDataSourceに@FlywayDataSource
を指定します。
@Bean
@Autowired
@FlywayDataSource
public DataSource secondaryDataSource(@Qualifier("secondaryProperties")
DataSourceProperties properties) {
return properties.initializeDataSourceBuilder().build();
}
サンプルソース
いつも通りGitHubに公開しています。
https://github.com/aha-oretama/spring-boot-multi-datasource-sample/tree/master
参照先
- http://docs.spring.io/spring-boot/docs/current/reference/html/howto-data-access.html
- http://docs.spring.io/spring-boot/docs/current/reference/html/howto-database-initialization.html
- http://www.baeldung.com/spring-data-jpa-multiple-databases
- http://dev.classmethod.jp/server-side/java/java-spring-boot-multiple-database/