SAAS 按租户分库方案
第一步
实现spring 的AbstractRoutingDataSource 抽象类:
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
/**
* Created by chenwenshun on 2018/12/12.
*/
public class RoutingDataSource extends AbstractRoutingDataSource{
@Override
protected Object determineCurrentLookupKey() {
return RoutingDataSourceContextHolder.get();
}
}
写一个线程容器保存每个请求的数据源信息:
/**
* Created by chenwenshun on 2018/12/12.
*/
public class RoutingDataSourceContextHolder {
private static final ThreadLocal<DataSourceEnum> threadlocalDataSourceKey = new ThreadLocal<>();
public static void set(DataSourceEnum key){
threadlocalDataSourceKey.set(key);
}
public static DataSourceEnum get(){
return threadlocalDataSourceKey.get();
}
public static void clear() {
threadlocalDataSourceKey.remove();
}
}
第二步
修改application.yml数据源配置:
datasource:
druid:
url: jdbc:mysql://111.231.82.13:6630/dict?useUnicode=true&characterEncoding=utf8
driver-class: com.mysql.jdbc.Driver
username: yanfa
password: yqKjKHX.1:bv
initial-size: 1
min-idle: 1
max-active: 20
test-on-borrow: true
filters: stat
datasource2:
druid:
url: jdbc:mysql://111.231.82.13:6630/dict2?useUnicode=true&characterEncoding=utf8
driver-class: com.mysql.jdbc.Driver
username: yanfa
password: yqKjKHX.1:bv
initial-size: 1
min-idle: 1
max-active: 20
test-on-borrow: true
filters: stat
对应的java 配置:
/**
* Created by chenwenshun on 2018/8/31.
*/
@Configuration
public class DataSourceConfig {
@Bean
@ConfigurationProperties(prefix = "datasource.druid")
public DataSource dataSource_0(){
return DataSourceBuilder.create()
.type(DruidDataSource.class)
.build();
}
@Bean
@ConfigurationProperties(prefix = "datasource2.druid")
public DataSource dataSource_1(){
return DataSourceBuilder.create()
.type(DruidDataSource.class)
.build();
}
@Bean
@Primary
public DataSource RoutingDataSource(
@Autowired @Qualifier("dataSource_0") DataSource dataSource_0,
@Autowired @Qualifier("dataSource_1") DataSource dataSource_1
){
Map<Object, Object> map = new HashMap<>();
map.put(DataSourceEnum.DS_0, dataSource_0);
map.put(DataSourceEnum.DS_1, dataSource_1);
RoutingDataSource routingDataSource = new RoutingDataSource();
routingDataSource.setTargetDataSources(map);
routingDataSource.setDefaultTargetDataSource(dataSource_0);
return routingDataSource;
}
第三步
实现一个商户与数据的映射逻辑,接口类似定义如下:
/**
* Created by chenwenshun on 2018/12/14.
* 商户与数据源的映射关系
* 具体项目不同实现
* 如:用数据库配置或者apollo,等方式
*/
public interface DataSourceMapping {
/**
* 商户ID 返回对应的数据源,可以采取两种方式
*
* 1、通过具体的配置
*
* 2、通过自己实现路由算法返回对应的数据源
* @param availableDataSources 可用数据源
* @param shareValue 业务ID如:商户ID
* @return 数据源标示
*/
String getDataSource(List<String> availableDataSources, String shareValue);
}
第四步
通过切面拦截所有controller 请求,获取商户ID,然后更具商户ID 调用DataSourceMapping.getDataSource 获取不同商户对应的数据源, 设置到RoutingDataSourceContextHolder 线程变量:
/**
* Created by chenwenshun on 2018/12/12.
*/
@Aspect
@Component
public class RoutingDataSourceAspect {
@Autowired
private DataSourceMapping dataSourceMapping;
@Pointcut("execution(public * com.freemud.springbootdemo.controller.*.*(..))")
public void point() {
}
@Before("point()")
public void doBefore(JoinPoint joinPoint) throws ClassNotFoundException, NotFoundException {
String classType = joinPoint.getTarget().getClass().getName();
// Class<?> clazz = Class.forName(classType);
// String clazzName = clazz.getName();
String methodName = joinPoint.getSignature().getName(); //获取方法名称
Object[] args = joinPoint.getArgs();//参数
Map<String,Object> nameAndArgs = this.getFieldsName(this.getClass(),classType,methodName,args);
String companyId = null;
if (nameAndArgs.containsKey("companyId")){
companyId = (String)nameAndArgs.get("companyId");
}else if (nameAndArgs.containsKey("requestBody")){
BaseRequest request = (BaseRequest)nameAndArgs.get("requestBody");
companyId = request.getCompanyId();
}else {
BaseRequest request = (BaseRequest)Lists.newArrayList(nameAndArgs.values()).get(0);
companyId = request.getCompanyId();
}
if (StringUtils.isBlank(companyId)){
throw new UnsupportedOperationException("companyId can not be null!");
}
String ds = DataSourceEnum.DS_0.name();
if (dataSourceMapping != null){
List<String> dataSourceList = Lists.newArrayList();
for ( DataSourceEnum dataSourceEnum :DataSourceEnum.values() ) {
dataSourceList.add( dataSourceEnum.name() );
}
ds = dataSourceMapping.getDataSource(dataSourceList , companyId) ;
}
RoutingDataSourceContextHolder.set(DataSourceEnum.valueOf(ds));
}
@After("point()")
public void doAfter(){
RoutingDataSourceContextHolder.clear();
}
private Map<String,Object> getFieldsName(Class cls, String clazzName, String methodName, Object[] args) throws NotFoundException {
Map<String,Object > map=new LinkedHashMap<>();
ClassPool pool = ClassPool.getDefault();
ClassClassPath classPath = new ClassClassPath(cls);
pool.insertClassPath(classPath);
CtClass cc = pool.get(clazzName);
CtMethod cm = cc.getDeclaredMethod(methodName);
MethodInfo methodInfo = cm.getMethodInfo();
CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
int pos = Modifier.isStatic(cm.getModifiers()) ? 0 : 1;
for (int i = 0; i < cm.getParameterTypes().length; i++){
map.put( attr.variableName(i + pos),args[i]);//paramNames即参数名
}
return map;
}
}
over ^0^
body { font-family: Helvetica, arial, sans-serif; font-size: 14px; line-height: 1.6; padding-top: 10px; padding-bottom: 10px; background-color: white; padding: 30px }
body>*:first-child { margin-top: 0 !important }
body>*:last-child { margin-bottom: 0 !important }
a { color: #4183C4 }
a.absent { color: #cc0000 }
a.anchor { display: block; padding-left: 30px; margin-left: -30px; cursor: pointer; position: absolute; top: 0; left: 0; bottom: 0 }
h1,h2,h3,h4,h5,h6 { margin: 20px 0 10px; padding: 0; font-weight: bold; cursor: text; position: relative }
h1:hover a.anchor,h2:hover a.anchor,h3:hover a.anchor,h4:hover a.anchor,h5:hover a.anchor,h6:hover a.anchor { background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA09pVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoMTMuMCAyMDEyMDMwNS5tLjQxNSAyMDEyLzAzLzA1OjIxOjAwOjAwKSAgKE1hY2ludG9zaCkiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6OUM2NjlDQjI4ODBGMTFFMTg1ODlEODNERDJBRjUwQTQiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6OUM2NjlDQjM4ODBGMTFFMTg1ODlEODNERDJBRjUwQTQiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo5QzY2OUNCMDg4MEYxMUUxODU4OUQ4M0REMkFGNTBBNCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo5QzY2OUNCMTg4MEYxMUUxODU4OUQ4M0REMkFGNTBBNCIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PsQhXeAAAABfSURBVHjaYvz//z8DJYCRUgMYQAbAMBQIAvEqkBQWXI6sHqwHiwG70TTBxGaiWwjCTGgOUgJiF1J8wMRAIUA34B4Q76HUBelAfJYSA0CuMIEaRP8wGIkGMA54bgQIMACAmkXJi0hKJQAAAABJRU5ErkJggg==") no-repeat 10px center; text-decoration: none }
h1 tt,h1 code { font-size: inherit }
h2 tt,h2 code { font-size: inherit }
h3 tt,h3 code { font-size: inherit }
h4 tt,h4 code { font-size: inherit }
h5 tt,h5 code { font-size: inherit }
h6 tt,h6 code { font-size: inherit }
h1 { font-size: 28px; color: black }
h2 { font-size: 24px; border-bottom: 1px solid #cccccc; color: black }
h3 { font-size: 18px }
h4 { font-size: 16px }
h5 { font-size: 14px }
h6 { color: #777777; font-size: 14px }
p,blockquote,ul,ol,dl,li,table,pre { margin: 15px 0 }
hr { background: transparent url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAAECAYAAACtBE5DAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBNYWNpbnRvc2giIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6OENDRjNBN0E2NTZBMTFFMEI3QjRBODM4NzJDMjlGNDgiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6OENDRjNBN0I2NTZBMTFFMEI3QjRBODM4NzJDMjlGNDgiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo4Q0NGM0E3ODY1NkExMUUwQjdCNEE4Mzg3MkMyOUY0OCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo4Q0NGM0E3OTY1NkExMUUwQjdCNEE4Mzg3MkMyOUY0OCIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PqqezsUAAAAfSURBVHjaYmRABcYwBiM2QSA4y4hNEKYDQxAEAAIMAHNGAzhkPOlYAAAAAElFTkSuQmCC") repeat-x 0 0; border: 0 none; color: #cccccc; height: 4px; padding: 0 }
body>h2:first-child { margin-top: 0; padding-top: 0 }
body>h1:first-child { margin-top: 0; padding-top: 0 }
body>h1:first-child+h2 { margin-top: 0; padding-top: 0 }
body>h3:first-child,body>h4:first-child,body>h5:first-child,body>h6:first-child { margin-top: 0; padding-top: 0 }
a:first-child h1,a:first-child h2,a:first-child h3,a:first-child h4,a:first-child h5,a:first-child h6 { margin-top: 0; padding-top: 0 }
h1 p,h2 p,h3 p,h4 p,h5 p,h6 p { margin-top: 0 }
li p.first { display: inline-block }
li { margin: 0 }
ul,ol { padding-left: 30px }
ul :first-child,ol :first-child { margin-top: 0 }
dl { padding: 0 }
dl dt { font-size: 14px; font-weight: bold; font-style: italic; padding: 0; margin: 15px 0 5px }
dl dt:first-child { padding: 0 }
dl dt>:first-child { margin-top: 0 }
dl dt>:last-child { margin-bottom: 0 }
dl dd { margin: 0 0 15px; padding: 0 15px }
dl dd>:first-child { margin-top: 0 }
dl dd>:last-child { margin-bottom: 0 }
blockquote { border-left: 4px solid #dddddd; padding: 0 15px; color: #777777 }
blockquote>:first-child { margin-top: 0 }
blockquote>:last-child { margin-bottom: 0 }
table { padding: 0; border-collapse: collapse }
table tr { border-top: 1px solid #cccccc; background-color: white; margin: 0; padding: 0 }
table tr:nth-child(2n) { background-color: #f8f8f8 }
table tr th { font-weight: bold; border: 1px solid #cccccc; margin: 0; padding: 6px 13px }
table tr td { border: 1px solid #cccccc; margin: 0; padding: 6px 13px }
table tr th :first-child,table tr td :first-child { margin-top: 0 }
table tr th :last-child,table tr td :last-child { margin-bottom: 0 }
img { max-width: 100% }
span.frame { display: block; overflow: hidden }
span.frame>span { border: 1px solid #dddddd; display: block; float: left; overflow: hidden; margin: 13px 0 0; padding: 7px; width: auto }
span.frame span img { display: block; float: left }
span.frame span span { clear: both; color: #333333; display: block; padding: 5px 0 0 }
span.align-center { display: block; overflow: hidden; clear: both }
span.align-center>span { display: block; overflow: hidden; margin: 13px auto 0; text-align: center }
span.align-center span img { margin: 0 auto; text-align: center }
span.align-right { display: block; overflow: hidden; clear: both }
span.align-right>span { display: block; overflow: hidden; margin: 13px 0 0; text-align: right }
span.align-right span img { margin: 0; text-align: right }
span.float-left { display: block; margin-right: 13px; overflow: hidden; float: left }
span.float-left span { margin: 13px 0 0 }
span.float-right { display: block; margin-left: 13px; overflow: hidden; float: right }
span.float-right>span { display: block; overflow: hidden; margin: 13px auto 0; text-align: right }
code,tt { margin: 0 2px; padding: 0 5px; white-space: nowrap; border: 1px solid #eaeaea; background-color: #f8f8f8 }
pre code { margin: 0; padding: 0; white-space: pre; border: none; background: transparent }
.highlight pre { background-color: #f8f8f8; border: 1px solid #cccccc; font-size: 13px; line-height: 19px; overflow: auto; padding: 6px 10px }
pre { background-color: #f8f8f8; border: 1px solid #cccccc; font-size: 13px; line-height: 19px; overflow: auto; padding: 6px 10px }
pre code,pre tt { background-color: transparent; border: none }
sup { font-size: 0.83em; vertical-align: super; line-height: 0 }
kbd { display: inline-block; padding: 3px 5px; font-size: 11px; line-height: 10px; color: #555; vertical-align: middle; background-color: #fcfcfc; border: solid 1px #ccc; border-bottom-color: #bbb }
* { }
原文地址:https://www.cnblogs.com/wenshun/p/10160960.html