springboot动态多数据源切换

发布于:2022-10-14 ⋅ 阅读:(382) ⋅ 点赞:(0)

1.创建DynamicDataSource并继承AbstractRoutingDataSource,实现determineCurrentLookupKey方法
public final class DynamicDataSource extends AbstractRoutingDataSource implements ApplicationContextAware {

    private static final String DATA_SOURCES_NAME = "targetDataSources";

    private ApplicationContext applicationContext;

    @Override
    protected Object determineCurrentLookupKey() {
        DataSourceBeanBuilder dataSourceBeanBuilder = DataSourceHolder.getDataSource();
        if (dataSourceBeanBuilder == null) {
            return null;
        }
        DataSourceBean dataSourceBean = new DataSourceBean(dataSourceBeanBuilder);
        try {
            Map<Object, Object> map = null;
            map = getTargetDataSources();

            synchronized (map) {
                if (!map.containsKey(dataSourceBean.getBeanName())) {
                    map.put(dataSourceBean.getBeanName(), createDataSource(dataSourceBean));
                    super.afterPropertiesSet();//通知spring有bean更新
                }
            }
        } catch (NoSuchFieldException e) {
            logger.info("failed:" + e);
        } catch (IllegalAccessException e) {
            logger.info("failed:" + e);
        }
        return dataSourceBean.getBeanName();

    }


    private Object createDataSource(DataSourceBean dataSourceBean) throws IllegalAccessException {
        //在spring容器中创建并且声明bean
        ConfigurableApplicationContext context = (ConfigurableApplicationContext) applicationContext;
        DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) context.getBeanFactory();
        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(DruidDataSource.class);
        //将dataSourceBean中的属性值赋给目标bean
        Map<String, Object> properties = getPropertyKeyValues(DataSourceBean.class, dataSourceBean);
        for (Map.Entry<String, Object> entry : properties.entrySet()) {
            beanDefinitionBuilder.addPropertyValue((String) entry.getKey(), entry.getValue());
        }
        beanFactory.registerBeanDefinition(dataSourceBean.getBeanName(), beanDefinitionBuilder.getBeanDefinition());
        return applicationContext.getBean(dataSourceBean.getBeanName());
    }

    private Map<Object, Object> getTargetDataSources() throws NoSuchFieldException, IllegalAccessException {
        Field field = AbstractRoutingDataSource.class.getDeclaredField(DATA_SOURCES_NAME);
        field.setAccessible(true);
        return (Map<Object, Object>) field.get(this);
    }


    private <T> Map<String, Object> getPropertyKeyValues(Class<T> clazz, Object object) throws IllegalAccessException {
        Field[] fields = clazz.getDeclaredFields();
        Map<String, Object> result = new HashMap<String, Object>();
        for (Field field : fields) {
            field.setAccessible(true);
            result.put(field.getName(), field.get(object));
        }
        result.remove("beanName");
        return result;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext=applicationContext;
    }
}
2.threadLocal保存数据库连接信息表,保证线程安全

public final class DataSourceHolder {
    private static ThreadLocal<DataSourceBeanBuilder> threadLocal=new ThreadLocal<DataSourceBeanBuilder>(){
        @Override
        protected DataSourceBeanBuilder initialValue() {
            return null;
        }
    };

    static DataSourceBeanBuilder getDataSource(){
        return threadLocal.get();
    }

    public static void setDataSource(DataSourceBeanBuilder dataSourceBeanBuilder){
        threadLocal.set(dataSourceBeanBuilder);
    }


    public static void clearDataSource(){
        threadLocal.remove();
    }
}
3.设置数据源上下文环境工具类
public class DataSourceContext {

    public static void setDataSource(DataSourceBeanBuilder dataSourceBeanBuilder) {
        DataSourceHolder.setDataSource(dataSourceBeanBuilder);
    }

    public static void clearDataSource() {
        DataSourceHolder.clearDataSource();
    }
}
4.测试
public List<Object> exectQuery4LimitQuery(DataSourceBeanBuilder dataSourceBeanBuilder, String resourceSql, long limitCount, long offset, String queryConditions, String path) {
        List<Object> queryResults = new ArrayList<>();
        DataSourceContext.setDataSource(dataSourceBeanBuilder);
        try {
            //region 查询数据
           
            //endregion 时间格式字段处理完毕
        } catch (Exception e) {
            logger.error(String.format("url %s query sql error for %s", resourceSql, e));
            throw new RuntimeException("querying database error");
        } finally {
            DataSourceContext.clearDataSource();
        }
        return queryResults;
    }
 

本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到