核心jar
conf:dataSource.yml
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.2.1</version>
</dependency>
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.16.16</version> </dependency>
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.26</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.0.12</version> </dependency>
<dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>1.3.1</version> </dependency> <dependency> <groupId>org.yaml</groupId> <artifactId>snakeyaml</artifactId> <version>1.17</version> </dependency>
@Configuration @EnableTransactionManagement public class MultiDataSource { @Bean public DynamicDataSource dynamicDataSource() { DynamicDataSource source = new DynamicDataSource(); return source; } @Bean public SqlSessionFactoryBean sqlSessionFactoryBean(DynamicDataSource source) { ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); SqlSessionFactoryBean sqlSessionFactory = new SqlSessionFactoryBean(); Resource[] resources ; try { resources = resolver.getResources("classpath*:META-INF/sqlmap/*Mapper.xml"); sqlSessionFactory.setDataSource(source); sqlSessionFactory.setMapperLocations(resources); } catch (IOException e) { e.printStackTrace(); } return sqlSessionFactory; } @Bean public MapperScannerConfigurer mapperScannerConfigurer() { MapperScannerConfigurer configurer = new MapperScannerConfigurer(); configurer.setBasePackage("com.fulihui.welfarecentre.dal.mapper"); configurer.setSqlSessionFactoryBeanName("sqlSessionFactoryBean"); return configurer; } }
初始化 mybatis相关配置
public class DataSourceContextHolder { private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>(); public static void setDataSource(String datasourceType){ contextHolder.set(datasourceType); } public static String getDataSource(){ return ((String) contextHolder.get()); } public static void clear(){ contextHolder.remove(); } }线程安全的TheadLocal来储存key
@Data public class CustomDruidDataSource extends DruidDataSource { private static final long serialVersionUID = 7144084830980132474L; private String key; }定义一个对象 继承阿里巴巴的druiDataSource
@Data public class DataSourcesConfig { private List<CustomDruidDataSource> dataSource; }因为是多个数据源 所以又创建了一个对象 来顶一个list来接收 自定义的数据源
下面来实现 dubbo服务和 web工程拿到的传来的key
1、 dubbo服务:
@Activate(group = Constants.PROVIDER) public class DubboContextFilter implements Filter { private final Logger logger = LoggerFactory.getLogger(DubboContextFilter.class); @Override public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException { Object[] arguments = invocation.getArguments(); if(arguments != null && arguments.length > 0){ for (Object argument : arguments) { if(argument instanceof CommonRequest){ checkArgs(((CommonRequest) argument).getShowModule()); }else if(argument instanceof CommonPageRequest){ checkArgs(((CommonPageRequest) argument).getShowModule()); } break; } } return invoker.invoke(invocation); } /** * 参数校验 * @param platformSource */ private void checkArgs(Integer platformSource){ logger.info("dubbo context filter: platformSource={}",platformSource); if(platformSource == null){ throw new RpcException("platformSource can't be empty!"); } DataSourceContextHolder.setDataSource(platformSource.toString()); } }
在resources目录下添加纯文本文件META-INF/dubbo/com.alibaba.dubbo.rpc.Filter,内容如下:
dubboContextFilter=xx.xx.DubboContextFilter2:web工程
使用一个动态代理,在执行这个方法的时候,我们动态的给DataSourceContextHolder设置值,我建议使用spring aop,或者如果
不是spring的项目,那么直接只用动态代理也是很好的我觉,下面贴出核心代码
@Aspect
@Component
public class DataSourceAspect {
@Pointcut("execution(* com.wang.route.DynamicPersonService.*(..))")
public void pointCut() {
}
@Before(value = "pointCut()")
public void before(JoinPoint joinPoint) {
DataSourceContextHolder.setDataSource(platformSource.toString());}} 切换动态数据源 核心配置:
public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { String dataSource = DataSourceContextHolder.getDataSource(); if (StringUtil.isBlank(dataSource)) { logger.info("determineCurrentLookupKey: dataSource not found!"); throw new RuntimeException("determineCurrentLookupKey: dataSource not found!"); } return DataSourceContextHolder.getDataSource(); } @Override public void afterPropertiesSet() { DataSourcesConfig config = null; Yaml yaml = new Yaml(); try { config = yaml.loadAs(new FileInputStream(new File("conf/dataSource.yml")), DataSourcesConfig.class); } catch (FileNotFoundException e) { e.printStackTrace(); } if (!Objects.isNull(config)) { Map<Object, Object> _targetDataSources = Maps.newHashMap(); config.getDataSource().forEach(dataSource -> { _targetDataSources.put(dataSource.getKey(), dataSource); }); super.setTargetDataSources(_targetDataSources); super.afterPropertiesSet(); } } }
conf:dataSource.yml
dataSource: - driverClassName: com.mysql.jdbc.Driver url: jdbc:mysql://192.168.1.45:3306/welfare username: root password: root1234 initialSize: 1 minIdle: 20 maxWait: 60000 timeBetweenEvictionRunsMillis: 60000 minEvictableIdleTimeMillis: 300000 validationQuery: SELECT 'x' testWhileIdle: true testOnBorrow: false poolPreparedStatements: false maxPoolPreparedStatementPerConnectionSize: 50 filters: stat defaultAutoCommit: true key: 0 - driverClassName: com.mysql.jdbc.Driver url: jdbc:mysql://192.168.1.45:3306/welfare_1 username: root password: root1234 initialSize: 1 minIdle: 20 maxWait: 60000 timeBetweenEvictionRunsMillis: 60000 minEvictableIdleTimeMillis: 300000 validationQuery: SELECT 'x' testWhileIdle: true testOnBorrow: false poolPreparedStatements: false maxPoolPreparedStatementPerConnectionSize: 50 filters: stat defaultAutoCommit: true key: 1