All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.jeesuite.mybatis.datasource.MultiRouteDataSource Maven / Gradle / Ivy
/**
*
*/
package com.jeesuite.mybatis.datasource;
import java.io.IOException;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.stream.Collectors;
import javax.sql.DataSource;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.env.Environment;
import org.springframework.jdbc.datasource.AbstractDataSource;
import org.springframework.jdbc.datasource.lookup.DataSourceLookup;
import org.springframework.jdbc.datasource.lookup.JndiDataSourceLookup;
import com.jeesuite.common.util.ResourceUtils;
import com.jeesuite.mybatis.MybatisConfigs;
import com.jeesuite.mybatis.datasource.builder.DruidDataSourceBuilder;
import com.jeesuite.mybatis.datasource.builder.HikariCPDataSourceBuilder;
import com.jeesuite.spring.InstanceFactory;
import com.jeesuite.spring.SpringInstanceProvider;
/**
* 自动路由多数据源(读写分离)
* @description
* @author vakin
* @date 2015年11月18日
* @Copyright (c) 2015, jwww
*/
public class MultiRouteDataSource extends AbstractDataSource implements ApplicationContextAware,InitializingBean{
private static final Logger logger = LoggerFactory.getLogger(MultiRouteDataSource.class);
private static final String MASTER_KEY = "master";
private DataSourceType dataSourceType = DataSourceType.Druid;
private ApplicationContext context;
private Map targetDataSources;
private DataSource defaultDataSource;
@Autowired
private Environment environment;
@Autowired(required = false)
private DataSourceConfigLoader dataSourceConfigLoader;
private DataSourceLookup dataSourceLookup = new JndiDataSourceLookup();
public void addTargetDataSources(Map targetDataSources) {
if(this.targetDataSources == null){
this.targetDataSources = targetDataSources;
}else{
this.targetDataSources.putAll(targetDataSources);
}
}
public void setDataSourceLookup(DataSourceLookup dataSourceLookup) {
this.dataSourceLookup = (dataSourceLookup != null ? dataSourceLookup : new JndiDataSourceLookup());
}
@Override
public void afterPropertiesSet() {
try {
dataSourceType = DataSourceType.valueOf(ResourceUtils.getProperty("db.dataSourceType", DataSourceType.Druid.name()));
} catch (Exception e) {
throw new IllegalArgumentException("Property 'db.dataSourceType' expect:" + Arrays.toString(DataSourceType.values()));
}
boolean tenantMode = MybatisConfigs.isTenantModeEnabled();
// 属性文件
Map map = new HashMap();
if(dataSourceConfigLoader != null){
List configs = dataSourceConfigLoader.load();
int slaveIndex = 0;
for (DataSourceConfig config : configs) {
if(!config.isMaster()){
config.setIndex(++slaveIndex);
}
buildDataSourceProperties(tenantMode,config,map);
}
}else{
if(tenantMode){
Properties properties = ResourceUtils.getAllProperties("tenant\\[.*\\]\\.master.*", false);
if(properties.isEmpty())throw new RuntimeException("tenant support Db config Like tenant[xxx].master.db.xxx");
List tenantIds = properties.keySet().stream().map(s -> {
return s.toString().split("\\[|\\]")[1];
}).distinct().collect(Collectors.toList());
for (String tenantId : tenantIds) {
parseDataSourceConfig(tenantId,map);
logger.info("Load tenant config finish, -> tenantId:{}",tenantId);
}
}else{
parseDataSourceConfig(null,map);
}
}
if(map.isEmpty())throw new RuntimeException("Db config Not found..");
registerDataSources(tenantMode,map);
if (this.targetDataSources == null || targetDataSources.isEmpty()) {
throw new IllegalArgumentException("Property 'targetDataSources' is required");
}
if (this.defaultDataSource == null && !tenantMode) {
throw new IllegalArgumentException("Property 'defaultDataSource' is required");
}
}
protected Object resolveSpecifiedLookupKey(Object lookupKey) {
return lookupKey;
}
protected DataSource resolveSpecifiedDataSource(Object dataSource) throws IllegalArgumentException {
if (dataSource instanceof DataSource) {
return (DataSource) dataSource;
}
else if (dataSource instanceof String) {
return this.dataSourceLookup.getDataSource((String) dataSource);
}
else {
throw new IllegalArgumentException(
"Illegal data source value - only [javax.sql.DataSource] and String supported: " + dataSource);
}
}
@Override
public Connection getConnection() throws SQLException {
return determineTargetDataSource().getConnection();
}
@Override
public Connection getConnection(String username, String password) throws SQLException {
return determineTargetDataSource().getConnection(username, password);
}
@Override
@SuppressWarnings("unchecked")
public T unwrap(Class iface) throws SQLException {
if (iface.isInstance(this)) {
return (T) this;
}
return determineTargetDataSource().unwrap(iface);
}
@Override
public boolean isWrapperFor(Class> iface) throws SQLException {
return (iface.isInstance(this) || determineTargetDataSource().isWrapperFor(iface));
}
protected DataSource determineTargetDataSource() {
Object lookupKey =MultiDataSourceManager.get().getDataSourceKey();
DataSource dataSource = targetDataSources.get(lookupKey);
if (dataSource == null) {
throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
}
return dataSource;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.context = applicationContext;
InstanceFactory.setInstanceProvider(new SpringInstanceProvider(context));
}
/**
* 功能说明:根据DataSource创建bean并注册到容器中
* @param mapCustom
* @param isLatestGroup
*/
private void registerDataSources(boolean tenantMode,Map mapCustom) {
DefaultListableBeanFactory acf = (DefaultListableBeanFactory) this.context.getAutowireCapableBeanFactory();
Iterator iter = mapCustom.keySet().iterator();
Map targetDataSources = new HashMap<>();
BeanDefinitionBuilder beanDefinitionBuilder = null;
while (iter.hasNext()) {
String dsKey = iter.next(); //
Properties nodeProps = mapCustom.get(dsKey);
// 如果当前库为最新一组数据库,注册beanName为master
logger.info(">>>>>begin to initialize datasource:" + dsKey + "\n================\n"
+ String.format("url:%s,username:%s", nodeProps.getProperty("url"),nodeProps.getProperty("username"))
+ "\n==============");
if (DataSourceType.Druid == dataSourceType) {
beanDefinitionBuilder = DruidDataSourceBuilder.builder(nodeProps);
} else if (DataSourceType.HikariCP == dataSourceType) {
beanDefinitionBuilder = HikariCPDataSourceBuilder.builder(nodeProps);
}
String beanName = dsKey.replaceAll("\\[|\\]|\\.", "") + "DataSource";
acf.registerBeanDefinition(beanName, beanDefinitionBuilder.getRawBeanDefinition());
DataSource ds = (DataSource) this.context.getBean(beanName);
targetDataSources.put(dsKey, ds);
// 设置默认数据源
if (dsKey.equals(MASTER_KEY)) {
defaultDataSource = ds;
}
logger.info("bean[" + dsKey + "] has initialized! lookupKey:" + dsKey);
//
MultiDataSourceManager.get().registerDataSourceKey(dsKey);
}
addTargetDataSources(targetDataSources);
}
/**
* 功能说明:解析配置,得到数据源Map
* @return
* @throws IOException
*/
private void parseDataSourceConfig(String tenantId,Map mapDataSource){
String tenantPrefix = tenantId == null ? "" : "tenant["+tenantId+"].";
String datasourceKey = tenantPrefix + MASTER_KEY;
parseEachConfig(datasourceKey,mapDataSource);
//解析同组下面的slave
int index = 1;
while(true){
datasourceKey = tenantPrefix + "slave" + index;
if(!ResourceUtils.containsProperty(datasourceKey + ".db.url") && !ResourceUtils.containsProperty(datasourceKey + ".db.jdbcUrl"))break;
parseEachConfig(datasourceKey,mapDataSource);
index++;
}
}
private void parseEachConfig(String keyPrefix,Map mapDataSource){
Properties properties = new Properties();
String prefix = "db.";
Properties tmpProps = ResourceUtils.getAllProperties(prefix);
String value;
for (Entry entry : tmpProps.entrySet()) {
value = environment.getProperty(entry.getKey().toString());
if(value == null)value = entry.getValue().toString();
properties.setProperty(entry.getKey().toString().replace(prefix, ""), value);
}
//
prefix = keyPrefix + ".db.";
tmpProps = ResourceUtils.getAllProperties(prefix);
for (Entry entry : tmpProps.entrySet()) {
value = environment.getProperty(entry.getKey().toString());
if(value == null)value = entry.getValue().toString();
properties.setProperty(entry.getKey().toString().replace(prefix, ""), value);
}
mapDataSource.put(keyPrefix, properties);
}
private void buildDataSourceProperties(boolean tenantMode,DataSourceConfig config, Map map) {
if(tenantMode && StringUtils.isBlank(config.getTenantId())){
throw new IllegalArgumentException("On tenantMode tenantId required ");
}
String dsKey = config.isMaster() ? MASTER_KEY : "slave" + config.getIndex();
if(tenantMode)dsKey = "tenant["+config.getTenantId()+"]." + dsKey;
Properties properties = new Properties();
properties.setProperty("url", config.getUrl());
properties.setProperty("username", config.getUsername());
properties.setProperty("password", config.getPassword());
String prefix = "db.";
Properties tmpProps = ResourceUtils.getAllProperties(prefix);
properties.putAll(tmpProps);
map.put(dsKey, properties);
}
}