背景:项目本来就是springboot+springJpa的框架,连接池用的德鲁伊DruidDataSource。
想要在数据库配置多数据源,用户可以随便新增数据源,根据请求带的不同参数,controller层随意切换数据源,这样就可以同一套部署的代码,操作不同的数据库,有点 SaaS的味道,不过是各自独立的库。
1.配置默认数据源
2.启动之后加载数据库的数据源 并且加载到bean交给spring管理(数据源增删改的时候也要执行一遍本方法)
3.controller里面指定数据源。
1.德鲁数据源常见的springboot配置
原有的配置是只有primaryDataSource 这个 并且加了 Primary注解。那么我们要加一个,默认Primary数据源指向一个动态数据源 即 dynamicDataSource。并且 注入我们的primaryDataSource。这样 DynamicDataSource里就有一个数据源了。代码如下。
DynamicDataSource继承了AbstractRoutingDataSource,AbstractRoutingDataSource是springboot提供的数据源路由。
@Configuration public class DSConfig { @Bean(name = "primaryDataSource") @Qualifier("primaryDataSource") @ConfigurationProperties(prefix = "spring.datasource.primary") public DataSource primaryDataSource(){ return DruidDataSourceBuilder.create().build(); } @Bean(name = "dynamicDataSource") @Primary public DynamicDataSource dynamicDataSource(@Qualifier("primaryDataSource")DataSource dataSource){ DynamicDataSource dynamicDataSource = new DynamicDataSource(); dynamicDataSource.myMap = new HashMap<>();//保存我们有的数据源,方便后面动态增加 dynamicDataSource.myMap.put("1",dataSource); dynamicDataSource.setTargetDataSources(dynamicDataSource.myMap);//父类的方法 dynamicDataSource.setDefaultTargetDataSource(dataSource);//父类的方法 return dynamicDataSource; } }
2.启动后加载数据库配置的数据源,新建一个类,注入查询数据源表的service和ApplicationContext。
用@PostConstruct 注解 来默认程序启动即执行。(增删改的时候同样执行类似代码,此处省略)
@Resource private SaasDataBase saasDataBase;
@Autowired private ApplicationContext ctx; /** * 初始化 */ @PostConstruct public void init() { //获取BeanFactory DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) ctx.getAutowireCapableBeanFactory(); //创建bean信息. //循环saasDataBase从数据库查出的数据源列表,此处省略 BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(DruidDataSource.class); beanDefinitionBuilder.addPropertyValue("url", "jdbc:oracle:thin:@ip:1521:orcl"); beanDefinitionBuilder.addPropertyValue("username", "XX"); beanDefinitionBuilder.addPropertyValue("password", "XX"); beanDefinitionBuilder.addPropertyValue("initialSize", 5); beanDefinitionBuilder.addPropertyValue("maxActive", 20); beanDefinitionBuilder.addPropertyValue("minIdle", 5); beanDefinitionBuilder.addPropertyValue("maxWait", 30000); //动态注册bean. bean的名字为第一个参数、 defaultListableBeanFactory.registerBeanDefinition("testDataSource", beanDefinitionBuilder.getBeanDefinition()); //获取动态注册的bean. DataSource testDataSource =ctx.getBean("testDataSource",DruidDataSource.class); dynamicDataSource.myMap.put("2",testDataSource);//指定key为2的数据源 dynamicDataSource.setTargetDataSources(dynamicDataSource.myMap); dynamicDataSource.afterPropertiesSet();//一定要执行这个方法 具体含义可以看父类代码
3.数据源已经都注入后,就剩下动态切换了,这个大家应该都看到过网上的案例
DynamicDataSource 和 DynamicDataSourceContextHolder 实现动态切换数据源的路由
public class DynamicDataSource extends AbstractRoutingDataSource { public Map
public class DynamicDataSourceContextHolder { /* * 当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本, * 所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。 */ private static final ThreadLocalcontextHolder = new ThreadLocal (); public static List dataSourceIds = new ArrayList (); public static void setDataSourceType(String dataSourceType) { contextHolder.set(dataSourceType); } public static String getDataSourceType() { return contextHolder.get(); } public static void clearDataSourceType() { contextHolder.remove(); } public static boolean containsDataSource(String dataSourceId){ return dataSourceIds.contains(dataSourceId); } }
以上两个类 就是针对AbstractRoutingDataSource的利用,根据我们指定的 key 1为primaryDataSource这个bean key2为我们指定的testDataSource 这个bean,在controller或者其他地方 直接set就可以了。
// DynamicDataSourceContextHolder.setDataSourceType("1"); // DynamicDataSourceContextHolder.setDataSourceType("2");
这里可以根据需要,根据判断条件随意设置。
以上3步写好之后,就完美实现了spingboot对springjpa的动态数据源切换。
springboot内置的默认EntityManager等东西不需要再具体配置,还是默认方式就行,因为他是基于@Primary 指定的这个 dynamicDataSource了。dynamicDataSource动态的变,一切都会随着变。