前置条件,你已经配置好spring boot+mybatis,能够参考以前的博客java
实现逻辑经过注解+aop切面编程来动态更新datasourcemysql
第一步,配置多个DataSourcegit
server: port: 8080 freezing: datasource: name: freezing url: jdbc:mysql://123.57.XXX.XXX:3636/freezingdb?characterEncoding=utf8&useSSL=false&serverTimezone=UTC username: freezing password: Zgjkj2015 # 使用druid数据源 type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.jdbc.Driver ucenter: datasource: name: ucenter url: jdbc:mysql://123.57..XXX.XXX:3636/ucenter?characterEncoding=utf8&useSSL=false&serverTimezone=UTC username: ucenter password: Zgjkj2015 # 使用druid数据源 type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.jdbc.Driver ## 该配置节点为独立的节点,有不少同窗容易将这个配置放在spring的节点下,致使配置没法被识别 mybatis: mapper-locations: classpath:mapping/*.xml #注意:必定要对应mapper映射xml文件的所在路径 type-aliases-package: com.hmkx.demo.model # 注意:对应实体类的路径
第二步,定义数据源上下文管理类spring
public class DynamicDataSourceContextHolder { private Logger logger = Logger.getLogger(DynamicDataSourceContextHolder.class); //存放当前线程使用的数据源类型信息 private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>(); //存放数据源id public static List<String> dataSourceIds = new ArrayList<String>(); //设置数据源 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 isContainsDataSource(String dataSourceId) { return dataSourceIds.contains(dataSourceId); } }
第三步,经过AbstractRoutingDataSource 继承修改这个类来动态修改dataTypesql
public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DynamicDataSourceContextHolder.getDataSourceType(); } }
第四步,DataSource注册数据库
public class DynamicDataSourceRegister implements ImportBeanDefinitionRegistrar, EnvironmentAware { private Logger logger = Logger.getLogger(DynamicDataSourceRegister.class); //指定数据源类型,我这里用的druid(springboot2.0默认数据源是hikari如何想使用其余数据源能够本身配置) // private static final String DATASOURCE_TYPE_DEFAULT = "com.zaxxer.hikari.HikariDataSource"; private static final String DATASOURCE_TYPE_DEFAULT = "com.alibaba.druid.pool.DruidDataSource"; //默认数据源 private DataSource defaultDataSource; //用户自定义数据源 private Map<String, DataSource> slaveDataSources = new HashMap<>(); @Override public void setEnvironment(Environment environment) { initDefaultDataSource(environment); initslaveDataSources(environment); } private void initDefaultDataSource(Environment env) { // 读取主数据源 Map<String, Object> dsMap = new HashMap<>(); dsMap.put("driver", env.getProperty("freezing.datasource.driver-class-name")); dsMap.put("url", env.getProperty("freezing.datasource.url")); dsMap.put("username", env.getProperty("freezing.datasource.username")); dsMap.put("password", env.getProperty("freezing.datasource.password")); logger.info(dsMap); defaultDataSource = buildDataSource(dsMap); } private void initslaveDataSources(Environment env) { // 读取配置文件获取更多数据源 /* String dsPrefixs = env.getProperty("slave.datasource.names"); for (String dsPrefix : dsPrefixs.split(",")) { // 多个数据源 Map<String, Object> dsMap = new HashMap<>(); dsMap.put("driver", env.getProperty("slave.datasource." + dsPrefix + ".driver")); dsMap.put("url", env.getProperty("slave.datasource." + dsPrefix + ".url")); dsMap.put("username", env.getProperty("slave.datasource." + dsPrefix + ".username")); dsMap.put("password", env.getProperty("slave.datasource." + dsPrefix + ".password")); DataSource ds = buildDataSource(dsMap); slaveDataSources.put(dsPrefix, ds); }*/ Map<String, Object> dsMap = new HashMap<>(); dsMap.put("driver", env.getProperty("ucenter.datasource.driver-class-name")); dsMap.put("url", env.getProperty("ucenter.datasource.url")); dsMap.put("username", env.getProperty("ucenter.datasource.username")); dsMap.put("password", env.getProperty("ucenter.datasource.password")); logger.info("ssssssssssssssssss"+dsMap); System.out.println("ssssssssssssssssss"+dsMap); DataSource ds = buildDataSource(dsMap); slaveDataSources.put("ucenter", ds); } @Override public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) { Map<Object, Object> targetDataSources = new HashMap<Object, Object>(); //添加默认数据源 targetDataSources.put("dataSource", this.defaultDataSource); DynamicDataSourceContextHolder.dataSourceIds.add("dataSource"); //添加其余数据源 targetDataSources.putAll(slaveDataSources); for (String key : slaveDataSources.keySet()) { DynamicDataSourceContextHolder.dataSourceIds.add(key); } //建立DynamicDataSource GenericBeanDefinition beanDefinition = new GenericBeanDefinition(); beanDefinition.setBeanClass(DynamicDataSource.class); beanDefinition.setSynthetic(true); MutablePropertyValues mpv = beanDefinition.getPropertyValues(); mpv.addPropertyValue("defaultTargetDataSource", defaultDataSource); mpv.addPropertyValue("targetDataSources", targetDataSources); //注册 - BeanDefinitionRegistry beanDefinitionRegistry.registerBeanDefinition("dataSource", beanDefinition); logger.info("Dynamic DataSource Registry"); } public DataSource buildDataSource(Map<String, Object> dataSourceMap) { try { Object type = dataSourceMap.get("type"); if (type == null) { type = DATASOURCE_TYPE_DEFAULT;// 默认DataSource } Class<? extends DataSource> dataSourceType; dataSourceType = (Class<? extends DataSource>) Class.forName((String) type); String driverClassName = dataSourceMap.get("driver").toString(); String url = dataSourceMap.get("url").toString(); String username = dataSourceMap.get("username").toString(); String password = dataSourceMap.get("password").toString(); // 自定义DataSource配置 DataSourceBuilder factory = DataSourceBuilder.create().driverClassName(driverClassName).url(url) .username(username).password(password).type(dataSourceType); return factory.build(); } catch (ClassNotFoundException e) { e.printStackTrace(); } return null; } }
到这里完成了,数据库动态切换的前置工做,接下来就是何时去切换了编程
第五步,自定义标签springboot
import java.lang.annotation.*; /** * @Author hht * @Date 2018-12-11 * @Description 做用于类、接口或者方法上 */ @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface TargetDataSource { String name(); }
第六步,监控标签的aopmybatis
/** * @Author hht * @Date 2018-12-11 * @Description 动态数据源通知 */ @Aspect @Order(-1)//保证在@Transactional以前执行 @Component public class DynamicDattaSourceAspect { private Logger logger = Logger.getLogger(DynamicDattaSourceAspect.class); //改变数据源 @Before("@annotation(targetDataSource)") public void changeDataSource(JoinPoint joinPoint, TargetDataSource targetDataSource) { String dbid = targetDataSource.name(); if (!DynamicDataSourceContextHolder.isContainsDataSource(dbid)) { //joinPoint.getSignature() :获取链接点的方法签名对象 logger.error("数据源 " + dbid + " 不存在使用默认的数据源 -> " + joinPoint.getSignature()); } else { logger.debug("使用数据源:" + dbid); DynamicDataSourceContextHolder.setDataSourceType(dbid); } } @After("@annotation(targetDataSource)") public void clearDataSource(JoinPoint joinPoint, TargetDataSource targetDataSource) { logger.debug("清除数据源 " + targetDataSource.name() + " !"); DynamicDataSourceContextHolder.clearDataSourceType(); } }
这时候基本完工,最后一步就是修改启动配置类app
package com.hmkx; import com.hmkx.config.DynamicDataSourceRegister; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Import; @SpringBootApplication @MapperScan("com.hmkx.mapper") @Import(DynamicDataSourceRegister.class) public class SpringMybatisDemoApplication { public static void main(String[] args) { SpringApplication.run(SpringMybatisDemoApplication.class, args); } }
完成了,下面是如何使用
@Service public class ArticleServiceImpl implements ArticleService { Logger log = LoggerFactory.getLogger(ArticleServiceImpl.class); @Resource private ArticlesMapper articlesMapper; @Override public Articles getArticleById(int id) { //使用默认数据库 Articles articles = articlesMapper.selectByPrimaryKey(501112); log.info(articles.getTitle()); return articles; } @Override @TargetDataSource(name="ucenter") public int updateByPrimaryKey(int id) { //使用ucenter数据库 ArticlesWithBLOBs art = new ArticlesWithBLOBs(); art.setId(501112); art.setTitle("测试"); int res = articlesMapper.updateByPrimaryKeySelective(art); return res; } }
记录完了,有错误或者更好的办法欢迎指代码下载地址 https://gitee.com/huhaitao/SpringBootMybatisForManyDatabase