需求:
编写一个代码生成器,前端下拉选择需要自动生成代码的数据库名。后端切换数据库并且生成对应的代码。
动态切换数据源:
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/**"); } }
执行自动生成代码逻辑。