本文共 6483 字,大约阅读时间需要 21 分钟。
在一些应用场景下,我们会使用到多个数据源,可能存在不同类型数据库,mysql和sql server之类的同时使用,还有就是主从数据库,读写分离也可能会使用多数据源来解决问题。使用的场景一般有:1,主从数据库切换;2,多租户间数据逻辑隔离
org.springframework.boot spring-boot-starter-web org.mybatis.spring.boot mybatis-spring-boot-starter 2.1.3 mysql mysql-connector-java runtime
/** * 动态数据源配置 */public class DynamicDataSource extends AbstractRoutingDataSource { /** * 数据源存储用map */ private final Map
代码中的dataSourceMap 是用来保存加入动态数据源中数据源,contextHolder 使用ThreadLocal 来定义,保证只为当前线程所持有。定义动态数据源一定要继承AbstractRoutingDataSource 类,并且重写determineCurrentLookupKey方法,determineCurrentLookupKey方法的返回值表示的是当前数据源的key,通过key去找已经加入map中的数据源。
/** * 数据源设定 * * @author DavidLei */@Configuration@MapperScan(basePackages = { "club.dlblog.datasource.mapper"}, sqlSessionFactoryRef = "sqlSessionFactory")public class DataSourceConfig { /** * 数据源设定 */ @Bean(name = "dataSource") @ConfigurationProperties("spring.datasource") public DataSource masterDataSource() { return DataSourceBuilder.create().build(); } /** * session工厂 */ @Bean(name = "sqlSessionFactory") public SqlSessionFactory sqlSessionFactory( @Qualifier("dynamicDataSource") DynamicDataSource dataSource) throws Exception { SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean(); sessionFactoryBean.setDataSource(dataSource); sessionFactoryBean.setMapperLocations( new PathMatchingResourcePatternResolver(). getResources("classpath:mapper/**/*.xml")); return sessionFactoryBean.getObject(); } /** * session模板 * @param sqlSessionFactory * @return */ @Bean(name = "sqlSessionTemplate") public SqlSessionTemplate ComSqlSessionTemplate( @Qualifier("sqlSessionFactory") SqlSessionFactory sqlSessionFactory) { return new SqlSessionTemplate(sqlSessionFactory); } /** * 动态数据源 * @param dataSource * @return */ @Bean(name = "dynamicDataSource") public DynamicDataSource dynamicDataSource(@Qualifier("dataSource") DataSource dataSource){ return new DynamicDataSource(dataSource); }}
在配置类中初始化动态数据源,并将将动态数据源绑定到session工厂上。
/** * 数据源切换工具 */public class DynamicDataSourceUtil { /** * 动态数据源 */ public static DynamicDataSource dynamicDataSource = null; /** * 判断当前数据源是否存在 * * @param tenantId * @return */ public static boolean isExistDataSource(String tenantId) { if (dynamicDataSource != null) { return dynamicDataSource.getDataSourceMap().containsKey(tenantId); } else { return false; } } /** * 切换数据源 * * @param key */ public static void setDataSourceKey(String key) { if (dynamicDataSource != null) { dynamicDataSource.getContextHolder().set(key); dynamicDataSource.afterPropertiesSet(); } } /** * 获取当前数据源 * * @return */ public static String getDataSourceKey() { if (dynamicDataSource != null) { return dynamicDataSource.getContextHolder().get(); } else { return null; } } /** * 切换到默认数据源 */ public static void clearDataSourceKey() { if (dynamicDataSource != null) { dynamicDataSource.getContextHolder().remove(); } } /** * 根据租户ID设置数据源 * * @param datatBase * @return */ public static void addDataSource(String datatBase, DataSource dataSource) { if (dynamicDataSource == null) { return; } // 没有数据源时添加数据源,有数据源直接使用 if (!isExistDataSource(datatBase)) { // 新增数据源 dynamicDataSource.getDataSourceMap().put(datatBase, dataSource); } // 切换数据源 checkoutDataSource(datatBase); } /** * 切换数据源 * * @param tenantId */ public static void checkoutDataSource(String tenantId) { if (dynamicDataSource != null) { // 切换数据源 setDataSourceKey(tenantId); dynamicDataSource.afterPropertiesSet(); } } /** * 创建数据源 * @param driverClassName * @param url * @param userName * @param password * @return */ public static DataSource createDataSource(String driverClassName,String url, String userName,String password){ return DataSourceBuilder.create().driverClassName(driverClassName) .url(url).username(userName).password(password).build(); }}
在工具类中就是对动态数据源中的存储数据源的map进行put和remove操作,指定数据源时只需将contextHolder重新设定。
在进行具体CRUD时,数据源就会根据contextHolder进行切换。createDataSource方法随时可以创建新的数据源,放入动态数据源中进行管理。定义过滤器,根据请求头来变更数据源
@Order(1)@WebFilter(urlPatterns = "/*")public class DynamicDataSourceFilter extends GenericFilter { private final static String TARGET_ID = "XXX-TARGET-ID"; @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) servletRequest; String targetId = request.getHeader(TARGET_ID); //数据源切换 DynamicDataSourceUtil.checkoutDataSource(targetId); filterChain.doFilter(servletRequest,servletResponse); }}
git地址 https://github.com/DavidLei08/BlogDataSource.git
转载地址:http://xhugn.baihongyu.com/