add multi datasource support

This commit is contained in:
开源海哥 2023-03-31 09:37:29 +08:00
parent c1340f256b
commit 3006ab30da
4 changed files with 159 additions and 197 deletions

View File

@ -0,0 +1,140 @@
/**
* Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com).
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.mybatisflex.spring.boot;
import com.mybatisflex.core.table.ConvertUtil;
import com.mybatisflex.core.util.StringUtil;
import org.apache.ibatis.reflection.Reflector;
import org.apache.ibatis.reflection.invoker.Invoker;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
public class DataSourceBuilder {
private static Map<String, String> dataSourceAlias = new HashMap<>();
static {
dataSourceAlias.put("druid", "com.alibaba.druid.pool.DruidDataSource");
dataSourceAlias.put("hikari", "com.zaxxer.hikari.HikariDataSource");
dataSourceAlias.put("hikaricp", "com.zaxxer.hikari.HikariDataSource");
dataSourceAlias.put("bee", "cn.beecp.BeeDataSource");
dataSourceAlias.put("beecp", "cn.beecp.BeeDataSource");
dataSourceAlias.put("dbcp", "org.apache.commons.dbcp2.BasicDataSource");
dataSourceAlias.put("dbcp2", "org.apache.commons.dbcp2.BasicDataSource");
}
private Map<String, String> dataSourceProperties;
public DataSourceBuilder(Map<String, String> dataSourceProperties) {
this.dataSourceProperties = dataSourceProperties;
}
public DataSource build() {
String dataSourceClassName = null;
String type = dataSourceProperties.get("type");
if (StringUtil.isNotBlank(type)) {
if (dataSourceAlias.containsKey(type)) {
dataSourceClassName = dataSourceAlias.get(type);
} else {
dataSourceClassName = type;
}
} else {
dataSourceClassName = detectDataSourceClass();
}
if (StringUtil.isBlank(dataSourceClassName)) {
throw new IllegalArgumentException("Cannot find the dataSource type: " + type);
}
try {
Class<?> dataSourceClass = Class.forName(dataSourceClassName);
Object dataSourceObject = dataSourceClass.newInstance();
setDataSourceProperties(dataSourceObject);
return (DataSource) dataSourceObject;
} catch (Exception e) {
throw new RuntimeException("Cannot new instance dataSource by class: " + dataSourceClassName);
}
}
private void setDataSourceProperties(Object dataSourceObject) throws Exception {
Reflector reflector = new Reflector(dataSourceObject.getClass());
for (String attr : dataSourceProperties.keySet()) {
String value = dataSourceProperties.get(attr);
String camelAttr = attrToCamel(attr);
if ("url".equals(camelAttr) || "jdbcUrl".equals(camelAttr)) {
if (reflector.hasSetter("url")) {
reflector.getSetInvoker("url").invoke(dataSourceObject, new Object[]{value});
} else if (reflector.hasSetter("jdbcUrl")) {
reflector.getSetInvoker("jdbcUrl").invoke(dataSourceObject, new Object[]{value});
}
} else {
if (reflector.hasSetter(camelAttr)) {
Invoker setInvoker = reflector.getSetInvoker(camelAttr);
setInvoker.invoke(dataSourceObject, new Object[]{ConvertUtil.convert(value, setInvoker.getType())});
}
}
}
}
public static String attrToCamel(String string) {
String temp = string.toLowerCase();
int strLen = temp.length();
StringBuilder sb = new StringBuilder(strLen);
for (int i = 0; i < strLen; i++) {
char c = temp.charAt(i);
if (c == '-') {
if (++i < strLen) {
sb.append(Character.toUpperCase(temp.charAt(i)));
}
} else {
sb.append(c);
}
}
return sb.toString();
}
private String detectDataSourceClass() {
String[] detectClassNames = new String[]{
"com.alibaba.druid.pool.DruidDataSource",
"com.zaxxer.hikari.HikariDataSource",
"cn.beecp.BeeDataSource",
"org.apache.commons.dbcp2.BasicDataSource",
};
for (String detectClassName : detectClassNames) {
String result = doDetectDataSourceClass(detectClassName);
if (result != null) {
return result;
}
}
return null;
}
private String doDetectDataSourceClass(String className) {
try {
Class.forName(className);
return className;
} catch (ClassNotFoundException e) {
return null;
}
}
}

View File

@ -1,70 +0,0 @@
/**
* Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com).
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.mybatisflex.spring.boot;
/**
* 参考 {@link org.springframework.boot.autoconfigure.jdbc.DataSourceProperties}
* 自定义 DataSourceProperty在未来可以提供更多的扩展
*/
public class DataSourceProperty {
private String type;
private String driverClassName;
private String url;
private String username;
private String password;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getDriverClassName() {
return driverClassName;
}
public void setDriverClassName(String driverClassName) {
this.driverClassName = driverClassName;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}

View File

@ -15,13 +15,8 @@
*/
package com.mybatisflex.spring.boot;
import cn.beecp.BeeDataSource;
import com.alibaba.druid.pool.DruidDataSource;
import com.mybatisflex.core.datasource.RoutingDataSource;
import com.mybatisflex.core.util.StringUtil;
import com.mybatisflex.spring.FlexSqlSessionFactoryBean;
import com.zaxxer.hikari.HikariDataSource;
import org.apache.commons.dbcp2.BasicDataSource;
import org.apache.ibatis.mapping.DatabaseIdProvider;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.scripting.LanguageDriver;
@ -31,8 +26,10 @@ import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.Resource;
@ -42,7 +39,10 @@ import org.springframework.util.StringUtils;
import javax.sql.DataSource;
import java.beans.PropertyDescriptor;
import java.util.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@ -55,25 +55,13 @@ import java.util.stream.Stream;
@ConditionalOnPropertyEmpty("spring.datasource.url")
@EnableConfigurationProperties(MybatisFlexProperties.class)
@AutoConfigureAfter({MybatisLanguageDriverAutoConfiguration.class})
@AutoConfigureBefore({DataSourceAutoConfiguration.class})
public class MultiDataSourceAutoConfiguration extends MybatisFlexAutoConfiguration {
private List<SqlSessionFactory> sqlSessionFactories = new ArrayList<>();
private List<DataSource> dataSources = new ArrayList<>();
private static Map<String, String> dataSourceAlias = new HashMap<>();
static {
dataSourceAlias.put("druid", "com.alibaba.druid.pool.DruidDataSource");
dataSourceAlias.put("hikari", "com.zaxxer.hikari.HikariDataSource");
dataSourceAlias.put("hikaricp", "com.zaxxer.hikari.HikariDataSource");
dataSourceAlias.put("bee", "cn.beecp.BeeDataSource");
dataSourceAlias.put("beecp", "cn.beecp.BeeDataSource");
dataSourceAlias.put("dbcp", "org.apache.commons.dbcp2.BasicDataSource");
dataSourceAlias.put("dbcp2", "org.apache.commons.dbcp2.BasicDataSource");
}
public MultiDataSourceAutoConfiguration(MybatisFlexProperties properties
, ObjectProvider<Interceptor[]> interceptorsProvider
, ObjectProvider<TypeHandler[]> typeHandlersProvider
@ -88,110 +76,14 @@ public class MultiDataSourceAutoConfiguration extends MybatisFlexAutoConfigurati
}
private void initDataSources(Map<String, DataSourceProperty> datasourceMap) {
private void initDataSources(Map<String, Map<String, String>> datasourceMap) {
if (datasourceMap != null) {
datasourceMap.forEach((s, dsp) -> {
SqlSessionFactory sqlSessionFactory = buildSqlSessionFactory(s, createDataSource(dsp));
DataSource dataSource = new DataSourceBuilder(dsp).build();
SqlSessionFactory sqlSessionFactory = buildSqlSessionFactory(s, dataSource);
sqlSessionFactories.add(sqlSessionFactory);
});
}
private DataSource createDataSource(DataSourceProperty dataSourceProperty) {
String type = dataSourceProperty.getType();
if (StringUtil.isBlank(type)) {
type = detectDataSourceClass();
}
if (StringUtil.isBlank(type)) {
throw new IllegalArgumentException("The dataSource cannot be null or empty.");
}
switch (type) {
case "druid":
case "com.alibaba.druid.pool.DruidDataSource":
return createDruidDataSource(dataSourceProperty);
case "hikari":
case "hikaricp":
case "com.zaxxer.hikari.HikariDataSource":
return createHikariDataSource(dataSourceProperty);
case "bee":
case "beecp":
case "cn.beecp.BeeDataSource":
return createBeeDataSource(dataSourceProperty);
case "dbcp":
case "dbcp2":
case "org.apache.commons.dbcp2.BasicDataSource":
return createDbcpDataSource(dataSourceProperty);
default:
throw new IllegalArgumentException("Cannot Support the dataSource type:" + dataSourceProperty.getType());
}
}
private String detectDataSourceClass() {
String[] detectClassNames = new String[]{
"com.alibaba.druid.pool.DruidDataSource",
"com.zaxxer.hikari.HikariDataSource",
"cn.beecp.BeeDataSource",
"org.apache.commons.dbcp2.BasicDataSource",
};
for (String detectClassName : detectClassNames) {
String result = doDetectDataSourceClass(detectClassName);
if (result != null) {
return result;
}
}
return null;
}
private String doDetectDataSourceClass(String className) {
try {
Class.forName(className);
return className;
} catch (ClassNotFoundException e) {
return null;
}
}
private DataSource createDbcpDataSource(DataSourceProperty dataSourceProperty) {
BasicDataSource ds = new BasicDataSource();
ds.setUrl(dataSourceProperty.getUrl());
ds.setUsername(dataSourceProperty.getUsername());
ds.setPassword(dataSourceProperty.getPassword());
ds.setDriverClassName(dataSourceProperty.getDriverClassName());
return ds;
}
private DataSource createBeeDataSource(DataSourceProperty dataSourceProperty) {
BeeDataSource ds = new BeeDataSource();
ds.setJdbcUrl(dataSourceProperty.getUrl());
ds.setUsername(dataSourceProperty.getUsername());
ds.setPassword(dataSourceProperty.getPassword());
ds.setDriverClassName(dataSourceProperty.getDriverClassName());
return ds;
}
private DataSource createDruidDataSource(DataSourceProperty dataSourceProperty) {
DruidDataSource ds = new DruidDataSource();
ds.setUrl(dataSourceProperty.getUrl());
ds.setUsername(dataSourceProperty.getUsername());
ds.setPassword(dataSourceProperty.getPassword());
ds.setDriverClassName(dataSourceProperty.getDriverClassName());
return ds;
}
private DataSource createHikariDataSource(DataSourceProperty dataSourceProperty) {
HikariDataSource ds = new HikariDataSource();
ds.setJdbcUrl(dataSourceProperty.getUrl());
ds.setUsername(dataSourceProperty.getUsername());
ds.setPassword(dataSourceProperty.getPassword());
if (StringUtil.isNotBlank(dataSourceProperty.getDriverClassName())) {
ds.setDriverClassName(dataSourceProperty.getDriverClassName());
}
return ds;
}
@ -264,14 +156,14 @@ public class MultiDataSourceAutoConfiguration extends MybatisFlexAutoConfigurati
@Bean
@ConditionalOnMissingBean
public SqlSessionFactory sqlSessionFactory() {
return sqlSessionFactories.get(0);
return sqlSessionFactories.isEmpty() ? null : sqlSessionFactories.get(0);
}
@Bean
@ConditionalOnMissingBean
public DataSource dataSource() {
return dataSources.get(0);
return dataSources.isEmpty() ? null : dataSources.get(0);
}

View File

@ -48,7 +48,7 @@ public class MybatisFlexProperties {
//多数据源的配置
//mybatis-flex.datasource.ds1.url=***
//mybatis-flex.datasource.ds2.url=***
private Map<String, DataSourceProperty> datasource;
private Map<String, Map<String,String>> datasource;
/**
* Location of MyBatis xml config file.
@ -102,11 +102,11 @@ public class MybatisFlexProperties {
*/
private CoreConfiguration configuration;
public Map<String, DataSourceProperty> getDatasource() {
public Map<String, Map<String, String>> getDatasource() {
return datasource;
}
public void setDatasource(Map<String, DataSourceProperty> datasource) {
public void setDatasource(Map<String, Map<String, String>> datasource) {
this.datasource = datasource;
}