テーブル名、構成が全く同じ複数DB(シャーディングなどしているような場合)
に対してSpringBootから参照する。
参考
以下を参考にさせていただきました。
[Spring Boot] 動的データソース変更
https://qiita.com/syukai/items/0d4bf27f82fef9965cdd
やること
・複数のDBを参照する(登録、更新はなし)
・テーブル構成が同じなので1つのEntityを使い回す
・リクエストのクエリ文字列によって参照先DBを切り替える
作成/修正クラス
・Config
・DynamicRoutingDataSourceResolver
・ContextHolder
・DBの切り替え
修正
Congfig
DataSourceConfig.java
package src.main.java.com.example.demo;
import java.util.Map;
import java.util.HashMap;
import javax.sql.DataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Configuration;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import com.zaxxer.hikari.HikariDataSource;
@Configuration
@EnableJpaRepositories(
basePackages = "jp.co.example.repositories",
entityManagerFactoryRef = "entityManagerFactory"
)
public class DataSourceConfig {
public HikariDataSource Node1() {
HikariDataSource ds = new HikariDataSource();
ds.setDriverClassName("com.mysql.cj.jdbc.Driver");
ds.setJdbcUrl("jdbc:mysql://XXX.XXX.XXX.XXX/node1");
ds.setUsername("");
ds.setPassword("");
return ds;
}
public HikariDataSource Node2() {
HikariDataSource ds = new HikariDataSource();
ds.setDriverClassName("com.mysql.cj.jdbc.Driver");
ds.setJdbcUrl("jdbc:mysql://XXX.XXX.XXX.XXX/node2");
ds.setUsername("");
ds.setPassword("");
return ds;
}
@Bean(name = "datasource")
@Primary
public DynamicRoutingDataSourceResolver datasource() {
DynamicRoutingDataSourceResolver resolver = new DynamicRoutingDataSourceResolver();
Map<Object, Object> dataSources = new HashMap<>();
dataSources.put("node1", Node1());
dataSources.put("node2", Node2());
resolver.setTargetDataSources(dataSources);
resolver.setDefaultTargetDataSource(Node1());
return resolver;
}
@Bean(name = "datasource")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder bulder, @Qualifier("datasource") DataSource dataSource) {
return bulder.dataSource(dataSource).packages("jp.co.example.entity").build();
}
}
DynamicRoutingDataSourceResolver
DynamicRoutingDataSourceResolver.java
package src.main.java.com.example.demo;
import java.util.Objects;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public class DynamicRoutingDataSourceResolver extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
if (Objects.equals("0", SchemaContextHolder.getSchemaType())) {
return "node1";
} else {
return "node2";
}
}
}
SchemaContextHolder
SchemaContextHolder.java
package src.main.java.com.example.demo;
public class SchemaContextHolder {
private static ThreadLocal<String> contextHolder = new ThreadLocal<>();
public static void setSchemaType(String partitionkey) { contextHolder.set(partitionkey); }
public static String getSchemaType() { return contextHolder.get(); }
}
DBの切り替え
package src.main.java.com.example.demo;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import src.main.java.com.example.demo.User;
public class DbDao implements Db {
@PersistenceContext
private EntityManager entityManager;
public List<User> getUsers(String partitionkey) {
SchemaContextHolder.setSchemaType(partitionkey);
return entityManager.createNativeQuery("SELECT * FROM USERS;", User.class).getResultList();
}
}