Spring bootでマルチデータソース対応の実装方法（MybatisとSpring Data JPA ）

Spring bootでマルチデータソースを利用する場合の実装方法を整理します。

##Mybatisの場合
Autoconfigを利用するので、application.propertiesにDBの接続情報を記載します。

```properties:application.properties
#プライマリーDB
spring.datasource.url=jdbc:mysql://localhost:3306/primary_db?useSSL=false
spring.datasource.username=demo
spring.datasource.password=demo
spring.datasource.driverClassName=com.mysql.jdbc.Driver

#セカンダリーDB
secondary.datasource.url=jdbc:mysql://localhost:3306/secondary_db?useSSL=false
secondary.datasource.username=demo
secondary.datasource.password=demo
secondary.datasource.driverClassName=com.mysql.jdbc.Driver
```

通常の通りにSpring bootのmainクラスを作成します。

```java:DemoApplication
@SpringBootApplication
public class DemoApplication {
	public static void main(String[] args) {
		SpringApplication.run(DemoApplication.class, args).close();
	}
}
```

プライマリーdataSourceとsqlSessionを定義します。
　・リポジトリクラスのbasepackage
　・SQLMapの格納フォルダ

```java:PrimaryDbConfig 
@Configuration
@MapperScan(basePackages = PrimaryDbConfig.BASE_PACKAGES
            , sqlSessionTemplateRef = "primarySqlSessionTemplate")
public class PrimaryDbConfig {
	public static final String BASE_PACKAGES = "com.example.demo.primary";
	public static final String MAPPER_XML_PATH = "classpath:com/example/demo/primary/*.xml";

	@Primary
	@Bean(name = "primaryDataSource")
	@ConfigurationProperties(prefix = "spring.datasource")
	public DataSource dataSource() {
		return new DataSource();
	}

	@Primary
	@Bean(name = "primarySqlSessionFactory")
	public SqlSessionFactory sqlSessionFactory(@Qualifier("primaryDataSource") DataSource primaryDataSource)
			throws Exception {
		SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
		bean.setDataSource(primaryDataSource);
		bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(MAPPER_XML_PATH));
		return bean.getObject();
	}

	@Bean(name = "primarySqlSessionTemplate")
	public SqlSessionTemplate sqlSessionTemplate(
			@Qualifier("primarySqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
		return new SqlSessionTemplate(sqlSessionFactory);
	}
}
```

上記と同様にセカンダリーdataSourceとsqlSessionを定義します。

```java:SecondaryDbConfig 
@Configuration
@MapperScan(basePackages = SecondaryDbConfig.BASE_PACKAGES
            , sqlSessionTemplateRef = "secondarySqlSessionTemplate")
public class SecondaryDbConfig {
	public static final String BASE_PACKAGES = "com.example.demo.secondary";
	public static final String MAPPER_XML_PATH = "classpath:com/example/demo/secondary/*.xml";
	
	@Bean(name = "secondaryDataSource")
	@ConfigurationProperties(prefix = "secondary.datasource")
	public DataSource dataSource() {
		return new DataSource();
	}

	@Bean(name = "secondarySqlSessionFactory")
	public SqlSessionFactory sqlSessionFactory(@Qualifier("secondaryDataSource") DataSource secondaryDataSource)
			throws Exception {
		SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
		bean.setDataSource(secondaryDataSource);
		bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(MAPPER_XML_PATH));
		return bean.getObject();
	}

	@Bean(name = "secondarySqlSessionTemplate")
	public SqlSessionTemplate sqlSessionTemplate(
			@Qualifier("secondarySqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
		return new SqlSessionTemplate(sqlSessionFactory);
	}
}
```

それぞれのリポジトリクラスを作成します。

```java:PrimaryRepository 
@Mapper
public interface PrimaryRepository {
	public long selectCountFromPrimary();
}
```

```java:SecondaryRepository
@Mapper
public interface SecondaryRepository {
	public long selectCountFromSecondary();
}
```

以下のリソースフォルダにプライマリーのSqlMapを定義します。

```
com
 |-example
     |-demo
         |-primary
```

```xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
        
<mapper namespace="com.example.demo.primary.PrimaryRepository">

    <select id="selectCountFromPrimary" resultType="long">
        select count(*) from emp;
    </select>
</mapper>
```


以下のリソースフォルダにセカンダリーのSqlMapを定義します。

```
com
 |-example
     |-demo
         |-secondary
```

```xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
        
<mapper namespace="com.example.demo.secondary.SecondaryRepository">

    <select id="selectCountFromSecondary" resultType="long">
        select count(*) from emp2;
    </select>
</mapper>
```

上記リポジトリを呼び出し、SQLを実行します。

```java
	@Autowired
    private PrimaryRepository primaryRepository;
	@Autowired
    private SecondaryRepository secondaryRepository;

    ...
    long countPrimary = primaryRepository.selectCountFromPrimary();
    long countSecondary = secondaryRepository.selectCountFromSecondary();
```

##Spring Data JPAの場合
Mybatisと同様にapplication.propertiesにDBの接続情報を記載します。

```properties:application.properties
#プライマリーDB
spring.datasource.url=jdbc:mysql://localhost:3306/primary_db?useSSL=false
spring.datasource.username=demo
spring.datasource.password=demo
spring.datasource.driverClassName=com.mysql.jdbc.Driver

#セカンダリーDB
secondary.datasource.url=jdbc:mysql://localhost:3306/secondary_db?useSSL=false
secondary.datasource.username=demo
secondary.datasource.password=demo
secondary.datasource.driverClassName=com.mysql.jdbc.Driver

spring.jpa.database=default
```

通常の通りにSpring bootのmainクラスを作成します。

```java:DemoApplication
@SpringBootApplication
public class DemoApplication {
	public static void main(String[] args) {
		SpringApplication.run(DemoApplication.class, args);
	}
}
```

プライマリーのdataSource、entityManagerとtransactionManagerを定義します。
　・リポジトリクラスのbasepackage
　・ドメインクラスのbasepackage

```java:PrimaryDbConfig 
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(entityManagerFactoryRef = "entityManagerFactory"
                       , transactionManagerRef = "transactionManager"
                       , basePackages = { "com.example.demo.primary" })
public class PrimaryDbConfig {

	@Primary
	@Bean(name = "dataSource")
	@ConfigurationProperties(prefix = "spring.datasource")
	public DataSource dataSource() {
		return DataSourceBuilder.create().build();
	}

	@Primary
	@Bean(name = "entityManagerFactory")
	public LocalContainerEntityManagerFactoryBean entityManagerFactory(
			EntityManagerFactoryBuilder builder,
			@Qualifier("dataSource") DataSource dataSource) {
		return builder.dataSource(dataSource).packages("com.example.demo.primary.domain").build();
	}

	@Primary
	@Bean(name = "transactionManager")
	public PlatformTransactionManager transactionManager(
			@Qualifier("entityManagerFactory") EntityManagerFactory entityManagerFactory) {
		return new JpaTransactionManager(entityManagerFactory);
	}
}
```

上記と同様にセカンダリーのdataSource、entityManagerとtransactionManagerを定義します。

```java:SecondaryDbConfig 
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(entityManagerFactoryRef = "secondaryEntityManagerFactory"
                       , transactionManagerRef = "secondaryTransactionManager"
                       , basePackages = {"com.example.demo.secondary" })
public class SecondaryDbConfig {

	@Bean(name = "secondaryDataSource")
	@ConfigurationProperties(prefix = "secondary.datasource")
	public DataSource dataSource() {
		return DataSourceBuilder.create().build();
	}

	@Bean(name = "secondaryEntityManagerFactory")
	public LocalContainerEntityManagerFactoryBean secondaryEntityManagerFactory(
			EntityManagerFactoryBuilder builder,
			@Qualifier("secondaryDataSource") DataSource dataSource) {
		return builder.dataSource(dataSource).packages("com.example.demo.secondary.domain").build();
	}

	@Bean(name = "secondaryTransactionManager")
	public PlatformTransactionManager secondaryTransactionManager(
			@Qualifier("secondaryEntityManagerFactory") EntityManagerFactory secondaryEntityManagerFactory) {
		return new JpaTransactionManager(secondaryEntityManagerFactory);
	}
}
```

それぞれのリポジトリクラスとドメインクラスを作成します。

```java:PrimaryRepository  
@Repository
public interface PrimaryRepository extends CrudRepository<Emp, String> {
	public long count();	
}
```

```java:SecondaryRepository 
@Repository
public interface SecondaryRepository extends CrudRepository<Emp2, String> {
	public long count();
}
```

```java:EmpDomain
@Entity
@Table(name="emp")
public class Emp {
	
	@Id
	@Column(name="emp_id")
	private String empId;

	@Column(name="dept_id")
	private String deptId;

	...
}
```


```java:EmpDomain2
@Entity
@Table(name="emp2")
public class Emp2 {
	
	@Id
	@Column(name="emp_id")
	private String empId;

	@Column(name="dept_id")
	private String deptId;

	...
```

Mybatisと同じの感じてリポジトリクラスを呼び出します。

```java
	@Autowired
    private PrimaryRepository primaryRepository;
	@Autowired
    private SecondaryRepository secondaryRepository;

    ...
    long countPrimary = primaryRepository.selectCountFromPrimary();
    long countSecondary = secondaryRepository.selectCountFromSecondary();
```