yml
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
master:
url: jdbc:mysql://localhost:3306/hzy-alibaba?characterEncoding=UTF-8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&useUnicode=true&serverTimezone=UTC
username: root
password: 123456
initial-size: 5
min-idle: 1
max-active: 100
test-on-borrow: true
slave:
url: jdbc:mysql://localhost:3306/hzy-alibaba2?characterEncoding=UTF-8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
username: root
password: 123456
initial-size: 5
min-idle: 1
max-active: 100
test-on-borrow: true
slave2:
url: jdbc:mysql://localhost:3306/hzy-alibaba3?characterEncoding=UTF-8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
username: root
password: 123456
initial-size: 5
min-idle: 1
max-active: 100
test-on-borrow: true
MybatisConfig
@Configuration
@MapperScan(basePackages = "com.hzy.alibaba.mapper")
public class MybatisConfig {
@Value("${spring.datasource.type}")
private Class<? extends DataSource> dataSourceType;
@Bean("masterDataSource")
@Primary
@ConfigurationProperties(prefix = "spring.datasource.master")
public DataSource masterDataSource() {
return DataSourceBuilder.create().type(dataSourceType).build();
}
@Bean("slaveDataSource")
@ConfigurationProperties(prefix = "spring.datasource.slave")
public DataSource slaveDataSource(){
return DataSourceBuilder.create().type(dataSourceType).build();
}
@Bean("slaveDataSource2")
@ConfigurationProperties(prefix = "spring.datasource.slave2")
public DataSource slaveDataSource2(){
return DataSourceBuilder.create().type(dataSourceType).build();
}
@Bean
public DynamicDataSource dataSource(@Qualifier("masterDataSource") DataSource masterDataSource,
@Qualifier("slaveDataSource") DataSource slaveDataSource,
@Qualifier("slaveDataSource2") DataSource slaveDataSource2) {
Map<Object, Object> dataSourceMap = new HashMap<>();
dataSourceMap.put(DataSourceType.MASTER, masterDataSource);
dataSourceMap.put(DataSourceType.SLAVE, slaveDataSource);
dataSourceMap.put(DataSourceType.SLAVE2, slaveDataSource2);
DynamicDataSource dynamicDataSource = new DynamicDataSource();
dynamicDataSource.setTargetDataSources(dataSourceMap);
dynamicDataSource.setDefaultTargetDataSource(masterDataSource);
// 将数据源的 key 放到数据源上下文的 key 集合中,用于切换时判断数据源是否有效
DynamicDataSourceContextHolder.dataSourceKeys.addAll(Lists.newArrayList(
DataSourceType.MASTER, DataSourceType.SLAVE, DataSourceType.SLAVE2));
// 将 Slave 数据源的 key 放在集合中,用于轮循
DynamicDataSourceContextHolder.slaveDataSourceKeys.addAll(Lists.newArrayList(
DataSourceType.SLAVE, DataSourceType.SLAVE2));
return dynamicDataSource;
}
@Bean
public SqlSessionFactory sqlSessionFactory(DynamicDataSource dynamicDataSource) throws Exception {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(dynamicDataSource);
factoryBean.setTypeAliasesPackage("com.hzy.alibaba.entity");
// 设置mapper.xml的位置路径
Resource[] resources = new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml");
factoryBean.setMapperLocations(resources);
return factoryBean.getObject();
}
@Bean
public PlatformTransactionManager transactionManager(DynamicDataSource dynamicDataSource){
return new DataSourceTransactionManager(dynamicDataSource);
}
}
DynamicDataSource
/**
* @author abc
* @description 动态数据源,每执行一次数据库,动态获取数据源
*/
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDataSource();
}
}
DruidConfig
@Configuration
public class DruidConfig {
@Bean
public ServletRegistrationBean statViewServlet() {
// 创建servlet注册实体
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new StatViewServlet(),
"/druid/*");
// 设置ip白名单
servletRegistrationBean.addInitParameter("allow", "127.0.0.1");
// 设置ip黑名单,如果allow与deny共同存在时,deny优先于allow
servletRegistrationBean.addInitParameter("deny", "192.168.0.1");
// 设置控制台管理用户
servletRegistrationBean.addInitParameter("loginUsername", "admin");
servletRegistrationBean.addInitParameter("loginPassword", "123456");
// 是否可以重置数据
servletRegistrationBean.addInitParameter("resetEnable", "false");
return servletRegistrationBean;
}
@Bean
public FilterRegistrationBean statFilter() {
// 创建过滤器
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new WebStatFilter());
// 设置过滤器过滤路径
filterRegistrationBean.addUrlPatterns("/*");
// 忽略过滤的形式
filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
return filterRegistrationBean;
}
}
DynamicDataSourceContextHolder
/**
* @author abc
* @description 动态数据源上下文管理:设置数据源,获取数据源,清除数据源
*/
public class DynamicDataSourceContextHolder {
private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class);
/**
* 用于在切换数据源时保证不会被其他线程修改
*/
private static Lock lock = new ReentrantLock();
/**
* 用于轮循的计数器
*/
private static int counter = 0;
/**
* Maintain variable for every thread, to avoid effect other thread
*/
private static final ThreadLocal<DataSourceType> CONTEXT_HOLDER = ThreadLocal.withInitial(() ->DataSourceType.MASTER);
/**
* All DataSource List
*/
public static List<DataSourceType> dataSourceKeys = new ArrayList<>();
/**
* The constant slaveDataSourceKeys.
*/
public static List<DataSourceType> slaveDataSourceKeys = new ArrayList<>();
/**
* To switch DataSource
*
* @param key the key
*/
public static void setDataSourceKey(DataSourceType key) {
CONTEXT_HOLDER.set(key);
}
/**
* Use master data source.
*/
public static void useMasterDataSource() {
CONTEXT_HOLDER.set(DataSourceType.MASTER);
}
/**
* 当使用只读数据源时通过轮循方式选择要使用的数据源
*/
public static void useSlaveDataSource() {
lock.lock();
try {
int datasourceKeyIndex = counter % slaveDataSourceKeys.size();
CONTEXT_HOLDER.set(slaveDataSourceKeys.get(datasourceKeyIndex));
counter++;
} catch (Exception e) {
logger.error("Switch slave datasource failed, error message is {}", e.getMessage());
useMasterDataSource();
e.printStackTrace();
} finally {
lock.unlock();
}
}
/**
* Get current DataSource
*
* @return data source key
*/
public static DataSourceType getDataSourceKey() {
return CONTEXT_HOLDER.get();
}
/**
* To set DataSource as default
*/
public static void clearDataSourceKey() {
CONTEXT_HOLDER.remove();
}
/**
* Check if give DataSource is in current DataSource list
*
* @param key the key
* @return boolean boolean
*/
public static boolean containDataSourceKey(DataSourceType key) {
return dataSourceKeys.contains(key);
}
}
DataSourceAspect
/***
* @author abc
* @desc 切换数据源
* @date 2019/11/26 0026 下午 14:12
*/
@Slf4j
@Aspect
@Component
public class DataSourceAspect {
private final String[] QUERY_PREFIX = {"select"};
@Pointcut("execution( * com.hzy.alibaba.mapper.*.*(..))")
public void daoAspect() {
}
@Before("daoAspect()")
public void switchDataSource(JoinPoint point) {
Boolean isQueryMethod = isQueryMethod(point.getSignature().getName());
if (isQueryMethod) {
DynamicDataSourceContextHolder.useSlaveDataSource();
log.info("Switch DataSource to [{}] in Method [{}]",
DynamicDataSourceContextHolder.getDataSourceKey(), point.getSignature());
}
}
@After("daoAspect()")
public void restoreDataSource(JoinPoint point) {
DynamicDataSourceContextHolder.clearDataSourceKey();
log.info("Restore DataSource to [{}] in Method [{}]",
DynamicDataSourceContextHolder.getDataSourceKey(), point.getSignature());
}
private Boolean isQueryMethod(String methodName) {
for (String prefix : QUERY_PREFIX) {
if (methodName.startsWith(prefix)) {
return true;
}
}
return false;
}
}
DataSource
/***
* @author abc
* @desc DataSource
* @date 2019/11/25 0025 下午 17:32
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface DataSource {
DataSourceType value() default DataSourceType.MASTER;
}
DataSourceType
/**
* @author abc
* @description 列出所有数据源
*/
public enum DataSourceType {
MASTER,SLAVE,SLAVE2
}
test
@DataSource(value = DataSourceType.MASTER)
网友评论