У меня есть приложение Spring, я хочу динамически изменять источник данных, расширяя AbstractRoutingDataSource
и переопределяя метод determineCurrentLookupKey
. Источник данных настраивается ниже:
@Bean
@Primary
public DataSource getMasterDataSource() {
return DataSourceBuilder.create()
.driverClassName("com.mysql.cj.jdbc.Driver")
.url("jdbc:mysql://localhost:3306/master")
.username("root")
.password("r00t")
.build();
}
@Bean
public DataSource getSlaveDataSource() {
return DataSourceBuilder.create()
.driverClassName("com.mysql.cj.jdbc.Driver")
.url("jdbc:mysql://localhost:3306/slave")
.username("root")
.password("r00t")
.build();
}
@Bean(name = "dynamicDataSource")
public DataSource dynamicDataSource() {
DynamicDataSource dynamicDataSource = new DynamicDataSource();
Map<Object, Object> dataSourceMap = new HashMap<>(2);
dataSourceMap.put(DBType.master.getName(), getMasterDataSource());
dataSourceMap.put(DBType.slave.getName(), getSlaveDataSource());
dynamicDataSource.setDefaultTargetDataSource(getMasterDataSource());
dynamicDataSource.setTargetDataSources(dataSourceMap);
DataSourceKeyThreadHolder.setDataSourceKey(DBType.master);
return dynamicDataSource;
}
@Bean
public SqlSessionFactory sqlSessionFactory(@Qualifier("dynamicDataSource") DataSource dataSource)
throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
return sqlSessionFactoryBean.getObject();
}
И я использую AOP для переключения источника данных:
@Pointcut("@annotation(com.gitlab.thamvannguyen.annotation.DataSourceKey)")
public void dynamicDataSource() {
}
@Before("dynamicDataSource()")
public void doBeforeCreateUser(JoinPoint joinpoint) {
Method method = ((MethodSignature) joinpoint.getSignature()).getMethod();
DataSourceKey dataSourceKey = method.getAnnotation(DataSourceKey.class);
if (dataSourceKey == null) {
dataSourceKey = joinpoint.getTarget().getClass().getAnnotation(DataSourceKey.class);
if (dataSourceKey == null) {
return;
}
}
DBType dbType = dataSourceKey.value();
DataSourceKeyThreadHolder.setDataSourceKey(dbType);
LOG.info("Dynamically switch data source, className " + joinpoint.getTarget().getClass()
+ ", methodName " + method.getName() + ", dataSourceKey " + dbType);
}
@After("dynamicDataSource()")
public void doAfterCreateUser(JoinPoint point) {
DataSourceKeyThreadHolder.clearDataSourceKey();
}
Тип БД:
public enum DBType {
master("master"), slave("slave");
private String name;
DBType(final String name) {
this.name = name;
}
public String getName() {
return name;
}
}
public class DynamicDataSource
extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceKeyThreadHolder.getDataSourceKey();
}
}
public class DataSourceKeyThreadHolder {
private static final Logger LOG = LoggerFactory.getLogger(DataSourceKeyThreadHolder.class);
private static final ThreadLocal<DBType> dataSourcesKeyHolder = new ThreadLocal<>();
public static String getDataSourceKey() {
LOG.info("Get data source key");
DBType type = dataSourcesKeyHolder.get();
if (type == null) {
type = DBType.master;
dataSourcesKeyHolder.set(type);
}
return type.getName();
}
public static void setDataSourceKey(DBType databaseType) {
Assert.notNull(databaseType, "DBType can not be null");
LOG.info("Set data source key");
dataSourcesKeyHolder.set(databaseType);
}
public static void clearDataSourceKey() {
LOG.info("Clear data source key");
}
}
My Service:
@DataSourceKey(value = DBType.master)
public User saveUser() {
User user = new User();
user.setName("Username");
return userRepository.save(user);
}
При сохранении данных в БД master , это успешно, но я пытаюсь переключиться на DB slave с аннотацией @DataSourceKey(value = DBType.slave)
в saveUser
метод. Он все еще сохраняет данные в db master . Любой может помочь!