需求:
编写一个代码生成器,前端下拉选择需要自动生成代码的数据库名。后端切换数据库并且生成对应的代码。
动态切换数据源:
springboot提供了一个AbstractRoutingDataSource类。我们可以实现一个类继承AbstractRoutingDataSource并且determineCurrentLookUpKey()方法。
具体步骤:
数据源配置
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
#MySQL配置
driverClassName: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/graduate?useUnicode=true&characterEncoding=UTF-8&useSSL=false
username: root
password: root
#MySQL配置
pqmanager:
driverClassName: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/hfb?useUnicode=true&characterEncoding=UTF-8&useSSL=false
username: root
password: root
pq37:
driverClassName: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/sys?useUnicode=true&characterEncoding=UTF-8&useSSL=false
username: root
password: root
pqmanager37:
driverClassName: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/srb_core?useUnicode=true&characterEncoding=UTF-8&useSSL=false
username: root
password: root
编写 DataSourceContextHolder设置和保存当前线程使用的数据源
public class DataSourceContextHolder {
//默认数据源
private static final String DEFAULT_DATASOURCE = "pq";
//保存线程连接的数据源
private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();
public static String getDataSource() {
return CONTEXT_HOLDER.get();
}
public static void setDataSource(String key) {
CONTEXT_HOLDER.set(key);
}
public static void cleanDataSource() {
CONTEXT_HOLDER.remove();
}
}
编写DynamicDataSource
public class DynamicDataSource extends AbstractRoutingDataSource {
/**
* @return 切换数据源的时候该方法会被调用
*/
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDataSource();
}
}
配置类
/**
* 多数据源配置类
*/
@Configuration
public class DataSourceConfig {
//@Primary
@Bean(name = "pq")
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource dataSourcePq() {
return new DruidDataSource();
}
@Bean("pq_37")
@ConfigurationProperties("spring.datasource.pq37")
public DataSource dataSourcePq37() {
return new DruidDataSource();
}
@Bean("pq_manager_37")
@ConfigurationProperties("spring.datasource.pqmanager37")
public DataSource dataSourcePqManager37() {
return new DruidDataSource();
}
@Primary //必须有一个数据源标记为Primary
@Bean("pq_manager")
@ConfigurationProperties("spring.datasource.pqmanager")
public DataSource dataSourcePqManager() {
return new DruidDataSource();
}
/**
* 数据源选择器 如果此处标记@Primary会导致循环依赖问题
*
* @return
*/
@Bean(name = "dynamicDataSource")
public DataSource dynamicDataSource(@Qualifier("pq") DataSource PqSource,
@Qualifier("pq_manager") DataSource PqManagerSource,
@Qualifier("pq_37") DataSource PqSource37,
@Qualifier("pq_manager_37") DataSource PqManagerSource37) {
DynamicDataSource dynamicDataSource = new DynamicDataSource();
//配置默认数据源
dynamicDataSource.setDefaultTargetDataSource(PqSource);
//保存所有可切换的数据源
Map<Object, Object> dataSourceMap = new HashMap<>();
dataSourceMap.put("pq", PqSource);
dataSourceMap.put("pq_manager", PqManagerSource);
dataSourceMap.put("pq_37", PqSource37);
dataSourceMap.put("pq_manager_37", PqManagerSource37);
dynamicDataSource.setTargetDataSources(dataSourceMap);
return dynamicDataSource;
}
}
@EnableTransactionManagement
@Configuration
public class MyBatisConfig {
@Resource(name = "dynamicDataSource")
private DataSource dynamicDataSource;
@Autowired
private MybatisProperties mybatisProperties;
@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dynamicDataSource);
sqlSessionFactoryBean.setMapperLocations(mybatisProperties.resolveMapperLocations());
sqlSessionFactoryBean.setConfiguration(mybatisProperties.getConfiguration());
return sqlSessionFactoryBean.getObject();
}
@Bean
public PlatformTransactionManager platformTransactionManager() {
return new DataSourceTransactionManager(dynamicDataSource);
}
}
利用拦截器拦截请求,查看请求参数是否存在某种参数(代表需要切换数据源,不存在则为默认数据源)。也可以使用AOP的方法作用于某个方法,
利用自定义注解配置需要切换的数据源,在切面那里只需利用反射得到对应的数据源在进行切换。
/**
* 拦截请求切换数据源
*/
public class DataSourceInterceptor extends HandlerInterceptorAdapter {
/**
* 拦截请求
*
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String dataSourceKey = request.getParameter("dataSourceKey");
System.out.println(dataSourceKey);
if (StringUtils.isNotEmpty(dataSourceKey)) {
DataSourceContextHolder.setDataSource(dataSourceKey);//重点
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
// System.out.println(DataSourceContextHolder.getDataSource());
DataSourceContextHolder.cleanDataSource();
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
super.afterCompletion(request, response, handler, ex);
}
}
配置拦截器
@Configuration
public class InterceptorsConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
InterceptorRegistration registration = registry.addInterceptor(new DataSourceInterceptor());
//拦截所有路径
registration.addPathPatterns("/sys/**");
}
}
执行自动生成代码逻辑。