yaml文件里的:
spring:
datasource:
one:
jdbc-url: jdbc:mysql://127.0.0.1:3306/bet?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true
username: root
password: root
two:
jdbc-url: jdbc:mysql://127.0.0.2:3306/bet?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true
username: root
password: root
three:
jdbc-url: jdbc:mysql://127.0.0.3:3306/bet?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true
username: root
password: root
package com.mybet.server.admin.annotation;
import java.lang.annotation.*;
/**
* 动态数据源注解
*/
@Target( { ElementType.METHOD, ElementType.TYPE } )
@Retention( RetentionPolicy.RUNTIME )
@Documented
public @interface DataSource {
/**
* 数据源key值
*
* @return
*/
String value();
}
package com.mybet.server.admin.aspect;
import com.mybet.server.admin.annotation.DataSource;
import com.mybet.server.admin.config.dds.DynamicDataSourceContextHolder;
import lombok.extern.log4j.Log4j2;
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.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
/**
* 动态数据源切换处理器
*/
@Log4j2
@Aspect
@Order( -1 ) // 该切面应当先于 @Transactional 执行
@Component
public class DynamicDataSourceAspect {
/**
* 切换数据源
*/
@Before( "@annotation(dataSource))" )
public void switchDataSource( JoinPoint point, DataSource dataSource ) {
if ( !DynamicDataSourceContextHolder.containDataSourceKey( dataSource.value() ) ) {
log.warn( "DataSource [{}] doesn't exist, use default DataSource [{}] " + dataSource.value() );
} else {
// 切换数据源
DynamicDataSourceContextHolder.setDataSourceKey( dataSource.value() );
log.warn( "Switch DataSource to [" + DynamicDataSourceContextHolder.getDataSourceKey()
+ "] in Method [" + point.getSignature() + "]" );
}
}
/**
* 重置数据源
*/
@After( "@annotation(dataSource))" )
public void restoreDataSource( JoinPoint point, DataSource dataSource ) {
// 将数据源置为默认数据源
DynamicDataSourceContextHolder.clearDataSourceKey();
log.warn( "Restore DataSource to [" + DynamicDataSourceContextHolder.getDataSourceKey()
+ "] in Method [" + point.getSignature() + "]" );
}
}
package com.mybet.server.admin.config.dds;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import javax.sql.DataSource;
import java.util.Map;
/**
* 动态数据源实现类
*/
public class DynamicDataSource extends AbstractRoutingDataSource {
/**
* 如果不希望数据源在启动配置时就加载好,可以定制这个方法,从任何你希望的地方读取并返回数据源 比如从数据库、文件、外部接口等读取数据源信息,并最终返回一个DataSource实现类对象即可
*/
@Override
protected DataSource determineTargetDataSource() {
return super.determineTargetDataSource();
}
/**
* 如果希望所有数据源在启动配置时就加载好,这里通过设置数据源Key值来切换数据,定制这个方法
*/
@Override
protected Object determineCurrentLookupKey() {
return DynamicDataSourceContextHolder.getDataSourceKey();
}
/**
* 设置默认数据源
*/
public void setDefaultDataSource( Object defaultDataSource ) {
super.setDefaultTargetDataSource( defaultDataSource );
}
/**
* 设置数据源
*/
public void setDataSources( Map<Object, Object> dataSources ) {
super.setTargetDataSources( dataSources );
// 将数据源的 key 放到数据源上下文的 key 集合中,用于切换时判断数据源是否有效
DynamicDataSourceContextHolder.addDataSourceKeys( dataSources.keySet() );
}
}
package com.mybet.server.admin.config.dds;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* 动态数据源上下文
*/
public class DynamicDataSourceContextHolder {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>() {
/**
* 将 master 数据源的 key作为默认数据源的 key
*/
@Override
protected String initialValue() {
return "dbdata";
}
};
/**
* 数据源的 key集合,用于切换时判断数据源是否存在
*/
public static List<Object> dataSourceKeys = new ArrayList<>();
/**
* 切换数据源
*/
public static void setDataSourceKey( String key ) {
contextHolder.set( key );
}
/**
* 获取数据源
*
* @return
*/
public static String getDataSourceKey() {
return contextHolder.get();
}
/**
* 重置数据源
*/
public static void clearDataSourceKey() {
contextHolder.remove();
}
/**
* 判断是否包含数据源
*
* @param key 数据源key
* @return
*/
public static boolean containDataSourceKey( String key ) {
return dataSourceKeys.contains( key );
}
/**
* 添加数据源keys
*
* @return
*/
public static boolean addDataSourceKeys( Collection<? extends Object> keys ) {
return dataSourceKeys.addAll( keys );
}
}
package com.mybet.server.admin.config;
import com.mybet.server.admin.config.dds.DynamicDataSource;
import com.zaxxer.hikari.HikariDataSource;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.mybatis.spring.boot.autoconfigure.MybatisProperties;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
@Configuration
@MapperScan( basePackages = { "com.mybet.server.admin.mapper" } ) // 扫描Mapper
public class MybatisConfig {
@Bean( "oneDataSource" )
@Primary
@ConfigurationProperties( prefix = "spring.datasource.one" )
public DataSource oneDataSource() {
return DataSourceBuilder.create().type( HikariDataSource.class ).build();
}
@Bean( "twoDataSource" )
@ConfigurationProperties( prefix = "spring.datasource.two" )
public DataSource twoDataSource() {
return DataSourceBuilder.create().type( HikariDataSource.class ).build();
}
@Bean( "threeDataSource" )
@ConfigurationProperties( prefix = "spring.datasource.three" )
public DataSource threeDataSource() {
return DataSourceBuilder.create().type( HikariDataSource.class ).build();
}
@Bean( "dynamicDataSource" )
public DataSource dynamicDataSource() {
DynamicDataSource dynamicDataSource = new DynamicDataSource();
Map<Object, Object> dataSourceMap = new HashMap<>();
dataSourceMap.put( "one", oneDataSource() );
dataSourceMap.put( "two", twoDataSource() );
dataSourceMap.put( "three", threeDataSource() );
// 将 master 数据源作为默认指定的数据源
dynamicDataSource.setDefaultDataSource( oneDataSource() );
// 将 master 和 slave 数据源作为指定的数据源
dynamicDataSource.setDataSources( dataSourceMap );
return dynamicDataSource;
}
@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean( @Qualifier( "dynamicDataSource" ) DataSource dynamicDataSource,
MybatisProperties mybatisProperties ) throws Exception {
SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
// 配置数据源,此处配置为关键配置,如果没有将 dynamicDataSource作为数据源则不能实现切换
sessionFactory.setDataSource( dynamicDataSource );
// 扫描Model
sessionFactory.setTypeAliasesPackage( "com.mybet.server.admin.domain" );
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
// 扫描映射文件
sessionFactory.setMapperLocations( resolver.getResources( "classpath*:mapper/*.xml" ) );
sessionFactory.setConfiguration( mybatisProperties.getConfiguration() );
return sessionFactory;
}
@Bean
public DataSourceTransactionManager transactionManager( @Qualifier( "dynamicDataSource" ) DataSource dynamicDataSource ) {
// 配置事务管理, 使用事务时在方法头部添加@Transactional注解即可
return new DataSourceTransactionManager( dynamicDataSource );
}
}
-----------------以上是配置,以下是业务使用------------------
package com.mybet.server.admin.utils;
import com.mybet.server.admin.config.dds.DynamicDataSourceContextHolder;
//指定数据源
public class DataSourceHolder {
public static void setDataSourceKey(String number){
String dataKey;
switch (number){
case "1":
dataKey = "one";
break;
case "2":
dataKey = "two";
break;
case "3":
dataKey = "three";
break;
default:
dataKey = "one";
}
DynamicDataSourceContextHolder.setDataSourceKey(dataKey);
}
//重置多数据源
public static void clearDataSourceKey(){
DynamicDataSourceContextHolder.clearDataSourceKey();
}
}