🌰

【Java / SpringBoot】MyBatisとJdbcTemplateのマルチデータソース(複数DB接続)を実現する方法

2024/10/14に公開

はじめに

この記事では、mybatisとJdbcTemplateを利用したマルチデータソース(複数DB接続)を実現する方法を記載しています。

この記事で書かないこと

・MyBatisやJdbcTemplateを利用した具体的な実装例について
・バッチ処理の実装方法について

前提

・SpringBootを利用した開発経験がある
・DB接続やSQL、ORMについて基礎的な知識を有している

対象者

・そもそもマルチデータソースとは?という方
・具体的なマルチデータソースが使用される例を知りたい方
・SpringBootでの開発にてマルチデータソースの実現方法を知りたい方

開発環境

・Java 21
・SpringBoot 3.3.1

マルチデータソースとは?

アプリケーションが複数のDBに接続し、それぞれのデータソース(DB)からデータを読み書きできる仕組みを指します。一般的には、1つのアプリケーションが1つのDBに接続することが多いですが、アプリケーションの要件によっては、複数のDBに接続する必要がある場合があり、その際にマルチデータソースを使用します。

具体的なマルチデータソースの使用例について

・大規模開発で複数のDBでデータをそれぞれ管理する必要があり、一つのアプリケーションから複数のDBに接続する場合
・バッチアプリケーションの開発にて、実際の処理に関連するデータを管理するDBとジョブの情報やステータスなどを管理するDBを分けたい場合

マルチデータソースの設定内容

※今回紹介する設定の例と詳細
・実際の処理に関するデータを格納するMySQLと、バッチの実行に関するメタデータを格納するH2DBのそれぞれに対してアクセスすることを想定。
・ORMツールについては、MySQLとのマッピングはMyBatis、H2DBとのマッピングはJdbcTemplateを使用。

実際のファイル

・application.properties

# MySQL
spring.datasource.mysql.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.mysql.url=jdbc:mysql://localhost:3306/mysql
spring.datasource.mysql.username=username
spring.datasource.mysql.password=password

# H2DB
spring.datasource.h2.driver-class-name=org.h2.Driver
spring.datasource.h2.url=jdbc:h2:mem:testdb
spring.datasource.h2.username=sa
spring.datasource.h2.password=

・DataSourceProperties.java

package com.demo.app.config;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import lombok.Data;

@Component
@ConfigurationProperties(prefix = "spring.datasource")
@Data
public class DataSourceProperties {
	
	private Mysql mysql = new Mysql();
	private H2 h2 = new H2();
	
	@Data
	public static class Mysql {
		private String driverClassName;
		private String url;
		private String username;
		private String password;
	}
	
	@Data
	public static class H2 {
		private String driverClassName;
		private String url;
		private String username;
		private String password;
	}
}

application.propertiesで定義したDB接続情報として、カスタムプロパティの動的に異なる部分("spring.datasource"以降)を補うための設定が書かれたファイル
【 "spring.datasource"以降のキー 】
・MySQL側
mysql.driver-class-name
mysql.url
mysql.username
mysql.password
・H2DB側
h2.driver-class-name
h2.url
h2.username
h2.password


・DataSourceConfig.java

package com.demo.app.config;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;

@Configuration
public class DataSourceConfig {
	
	@Autowired
	private DataSourceProperties dataSourceProperties;
	
	// MySQLの接続情報を登録
	@Bean(name = "mysqlDataSource")
	public DataSource mysqlDataSource() {
		DriverManagerDataSource dataSource = new DriverManagerDataSource();
		dataSource.setDriverClassName(dataSourceProperties.getMysql().getDriverClassName());
		dataSource.setUrl(dataSourceProperties.getMysql().getUrl());
		dataSource.setUsername(dataSourceProperties.getMysql().getUsername());
		dataSource.setPassword(dataSourceProperties.getMysql().getPassword());
		return dataSource;
	}

        // ※JdbcTemplateをマッピングツールとして使用しない場合、下記処理(mysqlJdbcTemplate)はなくてもOK
	@Bean(name = "mysqlJdbcTemplate")
	public JdbcTemplate mysqlJdbcTemplate(DataSource mysqlDataSource) {
		return new JdbcTemplate(mysqlDataSource);
	}
	
	// H2DBの接続情報を登録
	@Bean(name = "h2DataSource")
	public DataSource h2DataSource() {
		DriverManagerDataSource dataSource = new DriverManagerDataSource();
		dataSource.setDriverClassName(dataSourceProperties.getH2().getDriverClassName());
		dataSource.setUrl(dataSourceProperties.getH2().getUrl());
		dataSource.setUsername(dataSourceProperties.getH2().getUsername());
		dataSource.setPassword(dataSourceProperties.getH2().getPassword());
		return dataSource;
	}
	
	@Bean(name = "h2JdbcTemplate")
	public JdbcTemplate h2JdbcTemplate(DataSource h2DataSource) {
		return new JdbcTemplate(h2DataSource);
	}
	
}

下記のようにインジェクションすることで、DataSourceProperties.javaでの定義をもとに、application.propertisの内容がバインドされます。
@Autowired
private DataSourceProperties dataSourceProperties;


・MyBatisConfig.java

package com.demo.app.config;

import javax.sql.DataSource;

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@MapperScan("com.demo.app.domain.repository")
public class MyBatisConfig {
	
	@Bean
	public SqlSessionFactory mysqlSessionFactory(@Qualifier("mysqlDataSource") DataSource dataSource) throws Exception {
		SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
		factoryBean.setDataSource(dataSource);
		return factoryBean.getObject();
	}
	
	@Bean
	public SqlSessionTemplate mysqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
		return new SqlSessionTemplate(sqlSessionFactory);
	}

}

★ORMツールとしてMyBatisを採用する場合は上記の設定が必須。
【ポイント】
・@Qualifier("mysqlDataSource")を DataSource dataSourceに対して明示的に記載する必要がある。※これを忘れるとエラーに繋がるので注意!
⇨ @Qualifierに設定している"mysqlDataSource"については、DataSourceConfig.javaに定義しているMySQL接続情報のBean名。
・「@MapperScan("com.demo.app.domain.repository")」については、〇〇Repository.javaや〇〇Dao.javaといったSQL実行用のメソッドが定義されているクラスの階層を設定。

SQL実行用クラスのインスタンス生成の違い

・MyBatisの場合

@Autowired
SampleDao sampleDao;

※MyBatisConfig.javaのSqlSessionFactoryにて@Qualifierを使いDataSourceを明示的に指定しているため上記で改めて指定する必要はない。

・JdbcTemplateの場合

@Autowired
@Qualifier("h2JdbcTemplate")
private JdbcTemplate jdbcTemplate;

※DataSourceConfig.javaにてBeanとして登録したJdbcTemplate用のBean名を、明示的に指定してあげる必要がある。

おまけ(メタデータをH2DBで管理する方法)

下記内容を定義してあげることで、バッチ実行時に管理が必要なメタデータをH2DBに自動的に登録することが可能となるため、よければ参考にしてみてください。
・MetadataConfig.java

package com.demo.app.config;

import javax.sql.DataSource;

import org.springframework.batch.support.transaction.ResourcelessTransactionManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import org.springframework.transaction.PlatformTransactionManager;

@Configuration
public class MetadataConfig {
	
    @Bean
    public DataSource metaDataSource() {
        EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
        return builder.setType(EmbeddedDatabaseType.H2)
                .addScript("classpath:org/springframework/batch/core/schema-drop-h2.sql")
                .addScript("classpath:org/springframework/batch/core/schema-h2.sql")
                .build();
    }

    @Bean
    public PlatformTransactionManager transactionManager() {
        return new ResourcelessTransactionManager();
    }
}

最後に

改善の余地がある記事かと思いますが、マルチデータソースの対応が必要になった場合は是非参考にしていただきたいです。この記事を読んだことで、疑問や悩みなどの解消に少しでも繋げていただけたら大変嬉しく思います。最後までお読みいただきありがとうございました。

Discussion