参考原创地址:https://blog.csdn.net/neosmith/article/details/61202084java
说明:使用动态数据源的初衷,是能在应用层作到读写分离,即在程序代码中控制不一样的查询方法去链接不一样的库mysql
1.第一步 关闭spring boot的单数据源:spring
/** * 经过配置多数据源,实现动态数据源切换 * 1.禁用掉spring boot自带的数据源处理类DataSourceAutoConfiguration.class。 * 2.在配置文件application.yml中配置链接多个数据操做 */ @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}) @MapperScan("com.example.server.dao") public class ServerApplication { public static void main(String[] args) { SpringApplication.run(ServerApplication.class, args); }
2,第二步 定义一个ContextHolder, 用于保存当前线程使用的数据源名sql
/** * 使用动态数据源的初衷,是能在应用层作到读写分离,即在程序代码中控制不一样的查询方法去链接不一样的库。除了这种方法之外, * 数据库中间件也是个不错的选择,它的优势是数据库集群对应用来讲只暴露为单库,不须要切换数据源的代码逻辑。 * 咱们经过自定义注解 + AOP的方式实现数据源动态切换。 * 首先定义一个ContextHolder, 用于保存当前线程使用的数据源名 * @author liuqiuping * @date 2018-4-21 */ public class DataSourceContextHolder { public static final Logger log = LoggerFactory.getLogger(DataSourceContextHolder.class); /** * 默认数据源 */ public static final String DEFAULT_DS = "Master"; private static final ThreadLocal<String> contextHolder = new ThreadLocal<>(); // 设置数据源名 public static void setDB(String dbType) { log.debug("切换到{}数据源", dbType); contextHolder.set(dbType); } // 获取数据源名 public static String getDB() { return (contextHolder.get()); } // 清除数据源名 public static void clearDB() { contextHolder.remove(); } }
3 第三步 自定义一个javax.sql.DataSource接口的实现,这里只须要继承Spring为咱们预先实现好的父类AbstractRoutingDataSource数据库
package com.example.server.config; import com.alibaba.druid.pool.DruidDataSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.bind.RelaxedPropertyResolver; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; import org.springframework.transaction.annotation.EnableTransactionManagement; import javax.sql.DataSource; import java.util.HashMap; import java.util.Map; /** * 动态数据源配置, *自定义一个javax.sql.DataSource接口的实现,这里只须要继承Spring为咱们预先实现好的父类AbstractRoutingDataSource * @author liuqiuping * @date 2018-4-21 */ public class DynamicDataSource extends AbstractRoutingDataSource { private static final Logger log = LoggerFactory.getLogger(DynamicDataSource.class); @Override protected Object determineCurrentLookupKey() { log.debug("数据源为{}", DataSourceContextHolder.getDB()); return DataSourceContextHolder.getDB(); } }
4 第四步 建立自定义sqlSessionFactoryapache
import com.alibaba.druid.pool.DruidDataSource; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.SqlSessionTemplate; import org.mybatis.spring.annotation.MapperScan; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder; import org.springframework.boot.bind.RelaxedPropertyResolver; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import javax.annotation.Resource; import javax.sql.DataSource; import java.util.HashMap; import java.util.Map; /** * 建立自定义sqlSessionFactory,实现多个数据源动态注入sqlSessionFactory * @author liuqiuping * @date 2018-4-21 */ @Configuration @MapperScan(basePackages = {"com.example.server.dao"}, sqlSessionFactoryRef = "sqlSessionFactory") public class MybatisDbAConfig { private static final Logger logger = LoggerFactory.getLogger(DynamicDataSource.class); @Bean(name = "Master") @ConfigurationProperties(prefix = "spring.datasource.master") // application.properteis中对应属性的前缀 public DataSource MasterDataSource() { logger.info("注入 druid MasterDataSource!!!"); // DruidDataSource datasource = new DruidDataSource(); //// datasource.setUrl(propertyResolver.getProperty("url")); //// datasource.setDriverClassName(propertyResolver.getProperty("driver-class-name")); //// datasource.setUsername(propertyResolver.getProperty("username")); //// datasource.setPassword(propertyResolver.getProperty("password")); //// datasource.setInitialSize(Integer.valueOf(propertyResolver.getProperty("initialSize"))); //// datasource.setMinIdle(Integer.valueOf(propertyResolver.getProperty("minIdle"))); //// datasource.setMaxWait(Long.valueOf(propertyResolver.getProperty("maxWait"))); //// datasource.setMaxActive(Integer.valueOf(propertyResolver.getProperty("maxActive"))); //// datasource.setMinEvictableIdleTimeMillis(Long.valueOf(propertyResolver.getProperty("minEvictableIdleTimeMillis"))); //// return datasource; return DataSourceBuilder.create().build(); } @Bean(name = "Cluster") @ConfigurationProperties(prefix = "spring.datasource.cluster") // application.properteis中对应属性的前缀 public DataSource clusterDataSource() { logger.info("注入 druid MasterDataSource!!!"); // DruidDataSource datasource = new DruidDataSource(); // datasource.setUrl(propertyResolver.getProperty("url")); // datasource.setDriverClassName(propertyResolver.getProperty("driver-class-name")); // datasource.setUsername(propertyResolver.getProperty("username")); // datasource.setPassword(propertyResolver.getProperty("password")); // datasource.setInitialSize(Integer.valueOf(propertyResolver.getProperty("initialSize"))); // datasource.setMinIdle(Integer.valueOf(propertyResolver.getProperty("minIdle"))); // datasource.setMaxWait(Long.valueOf(propertyResolver.getProperty("maxWait"))); // datasource.setMaxActive(Integer.valueOf(propertyResolver.getProperty("maxActive"))); // datasource.setMinEvictableIdleTimeMillis(Long.valueOf(propertyResolver.getProperty("minEvictableIdleTimeMillis"))); // return datasource; return DataSourceBuilder.create().build(); } @Bean(name="dynamicDataSource") @Primary //优先使用,多数据源 public DataSource dataSource() { DynamicDataSource dynamicDataSource = new DynamicDataSource(); DataSource master = MasterDataSource(); DataSource slave = clusterDataSource(); //设置默认数据源 dynamicDataSource.setDefaultTargetDataSource(master); //配置多数据源 Map<Object,Object> map = new HashMap<>(); map.put("Master", master); //key须要跟ThreadLocal中的值对应 map.put("Cluster", slave); dynamicDataSource.setTargetDataSources(map); return dynamicDataSource; } }
5 第五步,编写AOP切面,实现切换逻辑,根据具体方法调用不用的数据源session
package com.example.server.config; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.stereotype.Component; import java.lang.reflect.Method; /** * 编写AOP切面,实现切换逻辑: * @author liuqiuping * @date 2018-4-21 */ @Aspect @Component public class DynamicDataSourceAspect { @Before("@annotation(DS)") public void beforeSwitchDS(JoinPoint point){ //得到当前访问的class Class<?> className = point.getTarget().getClass(); //得到访问的方法名 String methodName = point.getSignature().getName(); //获得方法的参数的类型 Class[] argClass = ((MethodSignature)point.getSignature()).getParameterTypes(); String dataSource = DataSourceContextHolder.DEFAULT_DS; try { // 获得访问的方法对象 Method method = className.getMethod(methodName, argClass); // 判断是否存在@DS注解 if (method.isAnnotationPresent(DS.class)) { DS annotation = method.getAnnotation(DS.class); // 取出注解中的数据源名 dataSource = annotation.value(); } } catch (Exception e) { e.printStackTrace(); } // 切换数据源 DataSourceContextHolder.setDB(dataSource); } @After("@annotation(DS)") public void afterSwitchDS(JoinPoint point){ DataSourceContextHolder.clearDB(); } }
6 第六步 自定义DS注解mybatis
/** * 自定义DS注解 * @author liuqiuping * @date 2018-4-21 * */ @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.METHOD }) public @interface DS { String value() default "master"; }
到此完成多数据源的配置app
接下来配置两个不一样数据数据源的配置链接ide
spring: application: name: sso-server ## 选择 数据源 datasource: master: url: jdbc:mysql://127.0.0.1:3306/sso?&useUnicode=true&characterEncoding=utf8 username: root password: 123 driver-class-name: com.mysql.jdbc.Driver initialSize: 5 minIdle: 5 maxActive: 20 maxWait: 60000 timeBetweenEvictionRunsMillis: 60000 minEvictableIdleTimeMillis: 300000 cluster: url: jdbc:mysql://127.0.0.1:3306/ssoCluster?&useUnicode=true&characterEncoding=utf8 username: root password: 123 driver-class-name: com.mysql.jdbc.Driver initialSize: 5 minIdle: 5 maxActive: 20 maxWait: 60000 timeBetweenEvictionRunsMillis: 60000 minEvictableIdleTimeMillis: 300000
动态数据源测试
@DS("Master") @Override public User findByLoginName(String name) { User user = userMapper.findByLoginName(name); if(null!=user){ user.setRoles(roleMapper.selectByUserId(user.getId())); } return user; } /** * 使用数据源2 * @param loginName * @return */ @DS("Cluster") @Override public User findByLoginNameTest(String loginName) { return userMapper.findByLoginNameTest(loginName); }
亲测,调用不一样的方法,执行不一样的数据查询成功。