✨ 添加新特性。pigx 动态切换数据源,为后期提供Online开发、数据同步以及读写分离做准备

This commit is contained in:
Lucky committed 2019-05-21 23:57:40 +08:00
1 parent 6eaa933fde
commit 896dbdeb40
12 files changed
+221 -15

No files matched your search

@@ -0,0 +1,16 @@
package com.pig4cloud.pigx.common.datasource;
import com.pig4cloud.pigx.common.datasource.config.DruidDataSourceProperties;
import lombok.AllArgsConstructor;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
/**
* @author Lucky
* @date 2019-05-18
* <p>
* DbAcess 自动配置类
*/
@AllArgsConstructor
@EnableConfigurationProperties({DruidDataSourceProperties.class})
public class DataSourceAutoConfiguration {
}
@@ -0,0 +1,20 @@
package com.pig4cloud.pigx.common.datasource.annotation;
import com.pig4cloud.pigx.common.datasource.config.DynamicDataSourceConfig;
import org.springframework.context.annotation.Import;
import java.lang.annotation.*;
/**
* @author Lucky
* @date 2019-05-18
* <p>
* 开启动态数据源
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({DynamicDataSourceConfig.class})
public @interface EnableDynamicDataSource {
}
@@ -0,0 +1,21 @@
package com.pig4cloud.pigx.common.datasource.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* @author lengleng
* @date 2019-05-14
* <p>
* 参考DruidDataSourceWrapper
*/
@Data
@Component
@ConfigurationProperties("spring.datasource.druid")
public class DruidDataSourceProperties {
private String username;
private String password;
private String url;
private String driverClassName;
}
@@ -0,0 +1,25 @@
package com.pig4cloud.pigx.common.datasource.config;
import com.pig4cloud.pigx.common.datasource.support.DynamicDataSourceContextHolder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
/**
* @author lengleng
* @date 2019-03-31
* <p>
* 动态数据源类
*/
@Slf4j
public class DynamicDataSource extends AbstractRoutingDataSource {
/**
* 指定路由Key
*
* @return
*/
@Override
protected Object determineCurrentLookupKey() {
return DynamicDataSourceContextHolder.getDataSourceType();
}
}
@@ -0,0 +1,100 @@
package com.pig4cloud.pigx.common.datasource.config;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import javax.annotation.PostConstruct;
import com.alibaba.druid.pool.DruidDataSource;
import com.mysql.cj.jdbc.Driver;
import com.pig4cloud.pigx.common.datasource.support.DataSourceConstants;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.jasypt.encryption.StringEncryptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.TransactionManagementConfigurer;
/**
* @author lengleng
* @date 2019-03-31
* <p>
* 动态数据源切换配置
*/
@Slf4j
@Configuration
@AllArgsConstructor
public class DynamicDataSourceConfig implements TransactionManagementConfigurer {
private final Map<Object, Object> dataSourceMap = new HashMap<>(8);
private final DruidDataSourceProperties dataSourceProperties;
private final StringEncryptor stringEncryptor;
@Bean("dynamicDataSource")
public DynamicDataSource dataSource() {
DynamicDataSource ds = new DynamicDataSource();
DruidDataSource cads = new DruidDataSource();
cads.setUrl(dataSourceProperties.getUrl());
cads.setDriverClassName(dataSourceProperties.getDriverClassName());
cads.setUsername(dataSourceProperties.getUsername());
cads.setPassword(dataSourceProperties.getPassword());
ds.setDefaultTargetDataSource(cads);
dataSourceMap.put(0, cads);
ds.setTargetDataSources(dataSourceMap);
return ds;
}
/**
* 组装默认配置的数据源,查询数据库配置
*/
@PostConstruct
public void init() {
DriverManagerDataSource dds = new DriverManagerDataSource();
dds.setUrl(dataSourceProperties.getUrl());
dds.setDriverClassName(dataSourceProperties.getDriverClassName());
dds.setUsername(dataSourceProperties.getUsername());
dds.setPassword(dataSourceProperties.getPassword());
List<Map<String, Object>> dbList = new JdbcTemplate(dds).queryForList(DataSourceConstants.QUERY_DS_SQL);
log.info("开始 -> 初始化动态数据源");
Optional.of(dbList).ifPresent(list -> list.forEach(db -> {
log.info("数据源:{}", db.get(DataSourceConstants.DS_NAME));
DruidDataSource ds = new DruidDataSource();
ds.setUrl(String.valueOf(db.get(DataSourceConstants.DS_JDBC_URL)));
ds.setDriverClassName(Driver.class.getName());
ds.setUsername((String) db.get(DataSourceConstants.DS_USER_NAME));
String decPwd = stringEncryptor.decrypt((String) db.get(DataSourceConstants.DS_USER_PWD));
ds.setPassword(decPwd);
dataSourceMap.put(db.get(DataSourceConstants.DS_ROUTE_KEY), ds);
}));
log.info("完毕 -> 初始化动态数据源,共计 {} 条", dataSourceMap.size());
}
/**
* 重新加载数据源配置
*/
public Boolean reload() {
init();
DynamicDataSource dataSource = dataSource();
dataSource.setTargetDataSources(dataSourceMap);
dataSource.afterPropertiesSet();
return Boolean.FALSE;
}
@Bean
public PlatformTransactionManager txManager() {
return new DataSourceTransactionManager(dataSource());
}
@Override
public PlatformTransactionManager annotationDrivenTransactionManager() {
return txManager();
}
}
@@ -0,0 +1,43 @@
package com.pig4cloud.pigx.common.datasource.config;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.pig4cloud.pigx.common.datasource.support.DynamicDataSourceContextHolder;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
/**
* @author lengleng
* @date 2019-05-18
* <p>
* 动态数据源拦截器
*/
@Slf4j
@Component
@AllArgsConstructor
public class DynamicDatasourceInterceptor implements HandlerInterceptor {
private final RedisTemplate redisTemplate;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
redisTemplate.setHashValueSerializer(new Jackson2JsonRedisSerializer<>(String.class));
HashOperations<String, String, String> vo = redisTemplate.opsForHash();
// String dbName = vo.get("db_route_key", 1);
DynamicDataSourceContextHolder.setDataSourceType(1);
log.info("数据源切换:{}", 1);
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable Exception ex) {
DynamicDataSourceContextHolder.clearDataSourceType();
}
}
@@ -0,0 +1,40 @@
package com.pig4cloud.pigx.common.datasource.support;
/**
* @author lengleng
* @date 2019-04-01
* <p>
* 数据源相关常量
*/
public interface DataSourceConstants {
/**
* 查询数据源的SQL
*/
String QUERY_DS_SQL = "select * from sys_datasource_conf where del_flag = 0";
/**
* 动态路由KEY
*/
String DS_ROUTE_KEY = "id";
/**
* 数据源名称
*/
String DS_NAME = "name";
/**
* jdbcurl
*/
String DS_JDBC_URL = "url";
/**
* 用户名
*/
String DS_USER_NAME = "username";
/**
* 密码
*/
String DS_USER_PWD = "password";
}
@@ -0,0 +1,40 @@
package com.pig4cloud.pigx.common.datasource.support;
import lombok.experimental.UtilityClass;
/**
* @author lengleng
* @date 2019-05-18
* <p>
* 根据当前线程来选择具体的数据源
*/
@UtilityClass
public class DynamicDataSourceContextHolder {
private final ThreadLocal<Integer> CONTEXT_HOLDER = new ThreadLocal<>();
/**
* 提供给AOP去设置当前的线程的数据源的信息
*
* @param dataSourceType
*/
public void setDataSourceType(Integer dataSourceType) {
CONTEXT_HOLDER.set(dataSourceType);
}
/**
* 提供给AbstractRoutingDataSource的实现类,通过key选择数据源
*
* @return
*/
public Integer getDataSourceType() {
return CONTEXT_HOLDER.get();
}
/**
* 使用默认的数据源
*
*/
public void clearDataSourceType() {
CONTEXT_HOLDER.remove();
}
}
@@ -0,0 +1,2 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.pig4cloud.pigx.common.datasource.DataSourceAutoConfiguration