add multi datasource support

This commit is contained in:
开源海哥 2023-03-31 13:27:12 +08:00
parent 6c1ddc3ac1
commit e89e8f3ef0
18 changed files with 197 additions and 250 deletions

View File

@ -36,9 +36,9 @@
</exclude> </exclude>
</excludes> </excludes>
</resource> </resource>
<resource> <!-- <resource>-->
<directory>target/generated-sources</directory> <!-- <directory>target/generated-sources</directory>-->
</resource> <!-- </resource>-->
</resources> </resources>
<plugins> <plugins>

View File

@ -16,7 +16,6 @@
package com.mybatisflex.core; package com.mybatisflex.core;
import com.mybatisflex.core.datasource.RoutingDataSource; import com.mybatisflex.core.datasource.RoutingDataSource;
import com.mybatisflex.core.dialect.DbType;
import com.mybatisflex.core.mybatis.FlexConfiguration; import com.mybatisflex.core.mybatis.FlexConfiguration;
import com.mybatisflex.core.mybatis.FlexSqlSessionFactoryBuilder; import com.mybatisflex.core.mybatis.FlexSqlSessionFactoryBuilder;
import org.apache.ibatis.logging.Log; import org.apache.ibatis.logging.Log;
@ -64,7 +63,6 @@ public class MybatisFlexBootstrap {
protected Configuration configuration; protected Configuration configuration;
protected List<Class<?>> mappers; protected List<Class<?>> mappers;
protected DbType dbType;
protected SqlSessionFactory sqlSessionFactory; protected SqlSessionFactory sqlSessionFactory;
protected Class<? extends Log> logImpl; protected Class<? extends Log> logImpl;
@ -115,7 +113,7 @@ public class MybatisFlexBootstrap {
transactionFactory = new JdbcTransactionFactory(); transactionFactory = new JdbcTransactionFactory();
} }
Environment environment = new Environment(environmentId, transactionFactory, new RoutingDataSource(environmentId, dataSource)); Environment environment = new Environment(environmentId, transactionFactory, dataSource);
configuration = new FlexConfiguration(environment); configuration = new FlexConfiguration(environment);
} }
@ -126,8 +124,6 @@ public class MybatisFlexBootstrap {
//init sqlSessionFactory //init sqlSessionFactory
this.sqlSessionFactory = new FlexSqlSessionFactoryBuilder().build(configuration); this.sqlSessionFactory = new FlexSqlSessionFactoryBuilder().build(configuration);
//init dbType
this.dbType = FlexGlobalConfig.getConfig(environmentId).getDbType();
//init mappers //init mappers
if (mappers != null) { if (mappers != null) {
@ -234,6 +230,18 @@ public class MybatisFlexBootstrap {
return this; return this;
} }
public MybatisFlexBootstrap addDataSource(String dataSourceKey, DataSource dataSource) {
if (this.dataSource == null) {
this.dataSource = new RoutingDataSource(dataSourceKey, dataSource);
} else if (this.dataSource instanceof RoutingDataSource) {
((RoutingDataSource) this.dataSource).addDataSource(dataSourceKey, dataSource);
} else {
this.dataSource = new RoutingDataSource("default", this.dataSource);
((RoutingDataSource) this.dataSource).addDataSource(dataSourceKey, dataSource);
}
return this;
}
public Configuration getConfiguration() { public Configuration getConfiguration() {
return configuration; return configuration;
} }
@ -249,15 +257,6 @@ public class MybatisFlexBootstrap {
} }
public DbType getDbType() {
return dbType;
}
public MybatisFlexBootstrap setDbType(DbType dbType) {
this.dbType = dbType;
return this;
}
public SqlSessionFactory getSqlSessionFactory() { public SqlSessionFactory getSqlSessionFactory() {
return sqlSessionFactory; return sqlSessionFactory;
} }

View File

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package com.mybatisflex.spring.boot; package com.mybatisflex.core.datasource;
import com.mybatisflex.core.table.ConvertUtil; import com.mybatisflex.core.table.ConvertUtil;
import com.mybatisflex.core.util.StringUtil; import com.mybatisflex.core.util.StringUtil;

View File

@ -21,22 +21,22 @@ public class DataSourceKey {
private static ThreadLocal<String> keyThreadLocal = new ThreadLocal<>(); private static ThreadLocal<String> keyThreadLocal = new ThreadLocal<>();
public static void use(String environmentId) { public static void use(String dataSourceKey) {
keyThreadLocal.set(environmentId); keyThreadLocal.set(dataSourceKey);
} }
public static <T> T use(String environmentId, Supplier<T> supplier) { public static <T> T use(String dataSourceKey, Supplier<T> supplier) {
try { try {
use(environmentId); use(dataSourceKey);
return supplier.get(); return supplier.get();
} finally { } finally {
clear(); clear();
} }
} }
public static void use(String environmentId, Runnable runnable) { public static void use(String dataSourceKey, Runnable runnable) {
try { try {
use(environmentId); use(dataSourceKey);
runnable.run(); runnable.run();
} finally { } finally {
clear(); clear();

View File

@ -15,6 +15,8 @@
*/ */
package com.mybatisflex.core.datasource; package com.mybatisflex.core.datasource;
import com.mybatisflex.core.dialect.DbType;
import com.mybatisflex.core.dialect.DbTypeUtil;
import com.mybatisflex.core.util.StringUtil; import com.mybatisflex.core.util.StringUtil;
import javax.sql.DataSource; import javax.sql.DataSource;
@ -25,13 +27,23 @@ import java.util.Map;
public class RoutingDataSource extends AbstractDataSource { public class RoutingDataSource extends AbstractDataSource {
private static Map<String, DataSource> dataSourceMap = new HashMap<>(); private final Map<String, DataSource> dataSourceMap = new HashMap<>();
private final Map<String, DbType> dbTypeHashMap = new HashMap<>();
private final DataSource defaultDataSource;
private DataSource delegate; public RoutingDataSource(String dataSourceKey, DataSource dataSource) {
this.defaultDataSource = dataSource;
dataSourceMap.put(dataSourceKey, dataSource);
dbTypeHashMap.put(dataSourceKey, DbTypeUtil.getDbType(dataSource));
}
public RoutingDataSource(String environmentId, DataSource delegate) { public void addDataSource(String dataSourceKey, DataSource dataSource) {
this.delegate = delegate; dataSourceMap.put(dataSourceKey, dataSource);
dataSourceMap.put(environmentId, delegate); dbTypeHashMap.put(dataSourceKey, DbTypeUtil.getDbType(dataSource));
}
public DbType getDbType(String dataSourceKey){
return dbTypeHashMap.get(dataSourceKey);
} }
@Override @Override
@ -44,22 +56,33 @@ public class RoutingDataSource extends AbstractDataSource {
return getDataSource().getConnection(username, password); return getDataSource().getConnection(username, password);
} }
@Override
@SuppressWarnings("unchecked")
public <T> T unwrap(Class<T> iface) throws SQLException {
if (iface.isInstance(this)) {
return (T) this;
}
return getDataSource().unwrap(iface);
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return (iface.isInstance(this) || getDataSource().isWrapperFor(iface));
}
private DataSource getDataSource() { private DataSource getDataSource() {
DataSource dataSource = delegate; DataSource dataSource = defaultDataSource;
if (dataSourceMap.size() > 1) { if (dataSourceMap.size() > 1) {
String environmentId = DataSourceKey.get(); String dataSourceKey = DataSourceKey.get();
if (StringUtil.isNotBlank(environmentId)) { if (StringUtil.isNotBlank(dataSourceKey)) {
dataSource = dataSourceMap.get(environmentId); dataSource = dataSourceMap.get(dataSourceKey);
if (dataSource == null) { if (dataSource == null) {
throw new IllegalStateException("Cannot get target DataSource for environmentId [" + environmentId + "]"); throw new IllegalStateException("Cannot get target DataSource for dataSourceKey [" + dataSourceKey + "]");
} }
} }
} }
return dataSource; return dataSource;
} }
public DataSource getDelegate() {
return delegate;
}
} }

View File

@ -16,6 +16,12 @@
package com.mybatisflex.core.dialect; package com.mybatisflex.core.dialect;
import com.mybatisflex.core.util.StringUtil;
import javax.sql.DataSource;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.regex.Pattern; import java.util.regex.Pattern;
/** /**
@ -24,6 +30,56 @@ import java.util.regex.Pattern;
public class DbTypeUtil { public class DbTypeUtil {
/**
* 获取当前配置的 DbType
*/
public static DbType getDbType(DataSource dataSource) {
String jdbcUrl = getJdbcUrl(dataSource);
if (StringUtil.isNotBlank(jdbcUrl)) {
return parseDbType(jdbcUrl);
}
throw new IllegalStateException("Cannot get dataSource jdbcUrl: " + dataSource.getClass().getName());
}
/**
* 通过数据源中获取 jdbc url 配置
* 符合 HikariCP, druid, c3p0, DBCP, beecp 数据源框架 以及 MyBatis UnpooledDataSource 的获取规则
* UnpooledDataSource 参考 @{@link UnpooledDataSource#getUrl()}
*
* @return jdbc url 配置
*/
private static String getJdbcUrl(DataSource dataSource) {
String[] methodNames = new String[]{"getUrl", "getJdbcUrl"};
for (String methodName : methodNames) {
try {
Method method = dataSource.getClass().getMethod(methodName);
return (String) method.invoke(dataSource);
} catch (Exception e) {
//ignore
}
}
Connection connection = null;
try {
connection = dataSource.getConnection();
return connection.getMetaData().getURL();
} catch (Exception e) {
//ignore
} finally {
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
}
}
}
return null;
}
/** /**
* 参考 druid MyBatis-plus JdbcUtils * 参考 druid MyBatis-plus JdbcUtils
* {@link com.alibaba.druid.util.JdbcUtils#getDbType(String, String)} * {@link com.alibaba.druid.util.JdbcUtils#getDbType(String, String)}
@ -32,7 +88,7 @@ public class DbTypeUtil {
* @param jdbcUrl jdbcURL * @param jdbcUrl jdbcURL
* @return 返回数据库类型 * @return 返回数据库类型
*/ */
public static DbType parseDbType(String jdbcUrl) { private static DbType parseDbType(String jdbcUrl) {
jdbcUrl = jdbcUrl.toLowerCase(); jdbcUrl = jdbcUrl.toLowerCase();
if (jdbcUrl.contains(":mysql:") || jdbcUrl.contains(":cobar:")) { if (jdbcUrl.contains(":mysql:") || jdbcUrl.contains(":cobar:")) {
return DbType.MYSQL; return DbType.MYSQL;

View File

@ -19,20 +19,14 @@ import com.mybatisflex.core.FlexGlobalConfig;
import com.mybatisflex.core.dialect.DbType; import com.mybatisflex.core.dialect.DbType;
import com.mybatisflex.core.dialect.DbTypeUtil; import com.mybatisflex.core.dialect.DbTypeUtil;
import com.mybatisflex.core.exception.FlexExceptions; import com.mybatisflex.core.exception.FlexExceptions;
import com.mybatisflex.core.util.StringUtil;
import org.apache.ibatis.datasource.unpooled.UnpooledDataSource;
import org.apache.ibatis.exceptions.ExceptionFactory; import org.apache.ibatis.exceptions.ExceptionFactory;
import org.apache.ibatis.executor.ErrorContext; import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.session.Configuration; import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import javax.sql.DataSource;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties; import java.util.Properties;
public class FlexSqlSessionFactoryBuilder extends SqlSessionFactoryBuilder { public class FlexSqlSessionFactoryBuilder extends SqlSessionFactoryBuilder {
@ -64,8 +58,7 @@ public class FlexSqlSessionFactoryBuilder extends SqlSessionFactoryBuilder {
} }
SqlSessionFactory sessionFactory = super.build(configuration); SqlSessionFactory sessionFactory = super.build(configuration);
DbType dbType = DbTypeUtil.getDbType(configuration.getEnvironment().getDataSource());
DbType dbType = getDbType(configuration);
//设置全局配置的 sessionFactory dbType //设置全局配置的 sessionFactory dbType
initGlobalConfig(configuration, sessionFactory, dbType); initGlobalConfig(configuration, sessionFactory, dbType);
@ -91,55 +84,5 @@ public class FlexSqlSessionFactoryBuilder extends SqlSessionFactoryBuilder {
} }
/**
* 获取当前配置的 DbType
*/
private DbType getDbType(Configuration configuration) {
DataSource dataSource = configuration.getEnvironment().getDataSource();
String jdbcUrl = getJdbcUrl(dataSource);
if (StringUtil.isNotBlank(jdbcUrl)) {
return DbTypeUtil.parseDbType(jdbcUrl);
}
throw new IllegalStateException("Cannot get dataSource jdbcUrl: " + dataSource.getClass().getName());
}
/**
* 通过数据源中获取 jdbc url 配置
* 符合 HikariCP, druid, c3p0, DBCP, beecp 数据源框架 以及 MyBatis UnpooledDataSource 的获取规则
* UnpooledDataSource 参考 @{@link UnpooledDataSource#getUrl()}
* TODO: 2023/2/18 可能极个别特殊的数据源无法获取 JDBC 配置的 URL
*
* @return jdbc url 配置
*/
private String getJdbcUrl(DataSource dataSource) {
String[] methodNames = new String[]{"getUrl", "getJdbcUrl"};
for (String methodName : methodNames) {
try {
Method method = dataSource.getClass().getMethod(methodName);
return (String) method.invoke(dataSource);
} catch (Exception e) {
//ignore
}
}
Connection connection = null;
try {
connection = dataSource.getConnection();
return connection.getMetaData().getURL();
} catch (Exception e) {
//ignore
} finally {
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
}
}
}
return null;
}
} }

View File

@ -18,22 +18,32 @@ package com.mybatisflex.core.mybatis;
import com.mybatisflex.annotation.UseDataSource; import com.mybatisflex.annotation.UseDataSource;
import com.mybatisflex.core.FlexGlobalConfig; import com.mybatisflex.core.FlexGlobalConfig;
import com.mybatisflex.core.datasource.DataSourceKey; import com.mybatisflex.core.datasource.DataSourceKey;
import com.mybatisflex.core.datasource.RoutingDataSource;
import com.mybatisflex.core.dialect.DbType; import com.mybatisflex.core.dialect.DbType;
import com.mybatisflex.core.dialect.DialectFactory; import com.mybatisflex.core.dialect.DialectFactory;
import com.mybatisflex.core.row.RowMapper;
import com.mybatisflex.core.table.TableInfo;
import com.mybatisflex.core.table.TableInfoFactory;
import com.mybatisflex.core.util.StringUtil; import com.mybatisflex.core.util.StringUtil;
import org.apache.ibatis.session.Configuration; import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.util.MapUtil;
import javax.sql.DataSource;
import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class MapperInvocationHandler implements InvocationHandler { public class MapperInvocationHandler implements InvocationHandler {
private static final String NONE_KEY = "!NONE";
private static final Map<Method, String> methodDataSourceKeyMap = new ConcurrentHashMap<>();
private Object mapper; private final Object mapper;
private Configuration configuration; private final DataSource dataSource;
public MapperInvocationHandler(Object mapper, Configuration configuration) { public MapperInvocationHandler(Object mapper, Configuration configuration) {
this.mapper = mapper; this.mapper = mapper;
this.configuration = configuration; this.dataSource = configuration.getEnvironment().getDataSource();
} }
@ -43,25 +53,26 @@ public class MapperInvocationHandler implements InvocationHandler {
boolean clearDbType = false; boolean clearDbType = false;
try { try {
//获取用户动态指定由用户指定数据源则应该有用户清除 //获取用户动态指定由用户指定数据源则应该有用户清除
String environmentId = DataSourceKey.get(); String dataSourceKey = DataSourceKey.get();
//通过 方法 的注解去获取 if (StringUtil.isBlank(dataSourceKey)) {
if (StringUtil.isBlank(environmentId)) { String methodKey = getMethodDataSource(method, proxy);
environmentId = getMethodEnvironmentId(method); if (!NONE_KEY.equals(methodKey)) {
if (StringUtil.isNotBlank(environmentId)) { dataSourceKey = methodKey;
DataSourceKey.use(environmentId); DataSourceKey.use(dataSourceKey);
clearDsKey = true; clearDsKey = true;
} }
} }
if (StringUtil.isBlank(environmentId)) {
environmentId = configuration.getEnvironment().getId();
}
//优先获取用户自己配置的 dbType //优先获取用户自己配置的 dbType
DbType dbType = DialectFactory.getHintDbType(); DbType dbType = DialectFactory.getHintDbType();
if (dbType == null) { if (dbType == null) {
dbType = FlexGlobalConfig.getConfig(environmentId).getDbType(); if (dataSourceKey != null && dataSource instanceof RoutingDataSource) {
dbType = ((RoutingDataSource) dataSource).getDbType(dataSourceKey);
}
if (dbType == null) {
dbType = FlexGlobalConfig.getDefaultConfig().getDbType();
}
DialectFactory.setHintDbType(dbType); DialectFactory.setHintDbType(dbType);
clearDbType = true; clearDbType = true;
} }
@ -77,9 +88,26 @@ public class MapperInvocationHandler implements InvocationHandler {
} }
private String getMethodEnvironmentId(Method method) { private static String getMethodDataSource(Method method, Object proxy) {
UseDataSource useDataSource = method.getAnnotation(UseDataSource.class); return MapUtil.computeIfAbsent(methodDataSourceKeyMap, method, method1 -> {
return useDataSource != null ? useDataSource.value() : null; UseDataSource useDataSource = method1.getAnnotation(UseDataSource.class);
if (useDataSource != null && StringUtil.isNotBlank(useDataSource.value())) {
return useDataSource.value();
} }
Class<?>[] interfaces = proxy.getClass().getInterfaces();
if (interfaces[0] != RowMapper.class) {
TableInfo tableInfo = TableInfoFactory.ofMapperClass(interfaces[0]);
if (tableInfo != null) {
String dataSourceKey = tableInfo.getDataSource();
if (StringUtil.isNotBlank(dataSourceKey)) {
return dataSourceKey;
}
}
}
return NONE_KEY;
});
}
} }

View File

@ -15,8 +15,6 @@
*/ */
package com.mybatisflex.core.row; package com.mybatisflex.core.row;
import com.mybatisflex.core.FlexGlobalConfig;
import com.mybatisflex.core.dialect.DbType;
import com.mybatisflex.core.paginate.Page; import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper; import com.mybatisflex.core.query.QueryWrapper;
import org.apache.ibatis.executor.BatchResult; import org.apache.ibatis.executor.BatchResult;
@ -32,12 +30,10 @@ import java.util.function.Function;
public class RowMapperInvoker { public class RowMapperInvoker {
private final SqlSessionFactory sqlSessionFactory; private final SqlSessionFactory sqlSessionFactory;
private final DbType dbType;
private RowSessionManager rowSessionManager = RowSessionManager.DEFAULT; private RowSessionManager rowSessionManager = RowSessionManager.DEFAULT;
public RowMapperInvoker(SqlSessionFactory sqlSessionFactory) { public RowMapperInvoker(SqlSessionFactory sqlSessionFactory) {
this.sqlSessionFactory = sqlSessionFactory; this.sqlSessionFactory = sqlSessionFactory;
this.dbType = FlexGlobalConfig.getConfig(sqlSessionFactory.getConfiguration()).getDbType();
} }
public RowSessionManager getRowSessionManager() { public RowSessionManager getRowSessionManager() {

View File

@ -66,7 +66,7 @@ public class TableInfoFactory {
public static TableInfo ofMapperClass(Class<?> mapperClass) { public static TableInfo ofMapperClass(Class<?> mapperClass) {
return MapUtil.computeIfAbsent(tableInfoMap, mapperClass, key -> { return MapUtil.computeIfAbsent(tableInfoMap, mapperClass, key -> {
Class<?> entityClass = getEntityClass(key); Class<?> entityClass = getEntityClass(mapperClass);
return entityClass != null ? ofEntityClass(entityClass) : null; return entityClass != null ? ofEntityClass(entityClass) : null;
}); });
} }

View File

@ -1,4 +1,4 @@
package com.mybatisflex.test; package com.mybatisflex.coretest;
import com.mybatisflex.annotation.Column; import com.mybatisflex.annotation.Column;
import com.mybatisflex.annotation.Id; import com.mybatisflex.annotation.Id;

View File

@ -1,4 +1,4 @@
package com.mybatisflex.test; package com.mybatisflex.coretest;
import com.mybatisflex.core.dialect.CommonsDialectImpl; import com.mybatisflex.core.dialect.CommonsDialectImpl;
import com.mybatisflex.core.dialect.IDialect; import com.mybatisflex.core.dialect.IDialect;
@ -13,8 +13,8 @@ import org.junit.Test;
import java.util.Arrays; import java.util.Arrays;
import static com.mybatisflex.core.query.QueryMethods.*; import static com.mybatisflex.core.query.QueryMethods.*;
import static com.mybatisflex.test.table.Tables.ACCOUNT; import static com.mybatisflex.coretest.table.Tables.ACCOUNT;
import static com.mybatisflex.test.table.Tables.ARTICLE; import static com.mybatisflex.coretest.table.Tables.ARTICLE;
public class AccountSqlTester { public class AccountSqlTester {

View File

@ -1,4 +1,4 @@
package com.mybatisflex.test; package com.mybatisflex.coretest;
import com.mybatisflex.annotation.Column; import com.mybatisflex.annotation.Column;
import com.mybatisflex.annotation.Id; import com.mybatisflex.annotation.Id;

View File

@ -1,4 +1,4 @@
package com.mybatisflex.test; package com.mybatisflex.coretest;
import com.mybatisflex.core.dialect.CommonsDialectImpl; import com.mybatisflex.core.dialect.CommonsDialectImpl;
import com.mybatisflex.core.dialect.IDialect; import com.mybatisflex.core.dialect.IDialect;
@ -8,7 +8,7 @@ import com.mybatisflex.core.table.TableInfoFactory;
import com.mybatisflex.core.util.CollectionUtil; import com.mybatisflex.core.util.CollectionUtil;
import org.junit.Test; import org.junit.Test;
import static com.mybatisflex.test.table.Tables.ARTICLE; import static com.mybatisflex.coretest.table.Tables.ARTICLE;
public class ArticleSqlTester { public class ArticleSqlTester {

View File

@ -15,36 +15,19 @@
*/ */
package com.mybatisflex.spring.boot; package com.mybatisflex.spring.boot;
import com.mybatisflex.core.datasource.DataSourceBuilder;
import com.mybatisflex.core.datasource.RoutingDataSource; import com.mybatisflex.core.datasource.RoutingDataSource;
import com.mybatisflex.spring.FlexSqlSessionFactoryBean;
import org.apache.ibatis.mapping.DatabaseIdProvider;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.scripting.LanguageDriver;
import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.type.TypeHandler;
import org.mybatis.spring.SqlSessionFactoryBean; 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.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import javax.sql.DataSource; import javax.sql.DataSource;
import java.beans.PropertyDescriptor;
import java.util.ArrayList;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/** /**
@ -54,116 +37,35 @@ import java.util.stream.Stream;
@ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class}) @ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class})
@ConditionalOnPropertyEmpty("spring.datasource.url") @ConditionalOnPropertyEmpty("spring.datasource.url")
@EnableConfigurationProperties(MybatisFlexProperties.class) @EnableConfigurationProperties(MybatisFlexProperties.class)
@AutoConfigureAfter({MybatisLanguageDriverAutoConfiguration.class})
@AutoConfigureBefore({DataSourceAutoConfiguration.class}) @AutoConfigureBefore({DataSourceAutoConfiguration.class})
public class MultiDataSourceAutoConfiguration extends MybatisFlexAutoConfiguration { public class MultiDataSourceAutoConfiguration {
private List<SqlSessionFactory> sqlSessionFactories = new ArrayList<>(); private Map<String, Map<String, String>> dataSourceProperties;
private List<DataSource> dataSources = new ArrayList<>();
public MultiDataSourceAutoConfiguration(MybatisFlexProperties properties public MultiDataSourceAutoConfiguration(MybatisFlexProperties properties) {
, ObjectProvider<Interceptor[]> interceptorsProvider dataSourceProperties = properties.getDatasource();
, ObjectProvider<TypeHandler[]> typeHandlersProvider
, ObjectProvider<LanguageDriver[]> languageDriversProvider
, ResourceLoader resourceLoader
, ObjectProvider<DatabaseIdProvider> databaseIdProvider
, ObjectProvider<List<ConfigurationCustomizer>> configurationCustomizersProvider
, ObjectProvider<List<SqlSessionFactoryBeanCustomizer>> sqlSessionFactoryBeanCustomizers) {
super(properties, interceptorsProvider, typeHandlersProvider, languageDriversProvider, resourceLoader, databaseIdProvider, configurationCustomizersProvider, sqlSessionFactoryBeanCustomizers);
initDataSources(properties.getDatasource());
}
private void initDataSources(Map<String, Map<String, String>> datasourceMap) {
if (datasourceMap != null) {
datasourceMap.forEach((s, dsp) -> {
DataSource dataSource = new DataSourceBuilder(dsp).build();
SqlSessionFactory sqlSessionFactory = buildSqlSessionFactory(s, dataSource);
sqlSessionFactories.add(sqlSessionFactory);
});
}
}
public SqlSessionFactory buildSqlSessionFactory(String environmentId, DataSource dataSource) {
// SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
SqlSessionFactoryBean factory = new FlexSqlSessionFactoryBean();
dataSource = new RoutingDataSource(environmentId, dataSource);
dataSources.add(dataSource);
factory.setDataSource(new RoutingDataSource(environmentId, dataSource));
// factory.setEnvironment(environmentId);
if (properties.getConfiguration() == null || properties.getConfiguration().getVfsImpl() == null) {
factory.setVfs(SpringBootVFS.class);
}
if (StringUtils.hasText(this.properties.getConfigLocation())) {
factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));
}
applyConfiguration(factory);
if (this.properties.getConfigurationProperties() != null) {
factory.setConfigurationProperties(this.properties.getConfigurationProperties());
}
if (!ObjectUtils.isEmpty(this.interceptors)) {
factory.setPlugins(this.interceptors);
}
if (this.databaseIdProvider != null) {
factory.setDatabaseIdProvider(this.databaseIdProvider);
}
if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) {
factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());
}
if (this.properties.getTypeAliasesSuperType() != null) {
factory.setTypeAliasesSuperType(this.properties.getTypeAliasesSuperType());
}
if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) {
factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());
}
if (!ObjectUtils.isEmpty(this.typeHandlers)) {
factory.setTypeHandlers(this.typeHandlers);
}
Resource[] mapperLocations = this.properties.resolveMapperLocations();
if (!ObjectUtils.isEmpty(mapperLocations)) {
factory.setMapperLocations(mapperLocations);
}
Set<String> factoryPropertyNames = Stream
.of(new BeanWrapperImpl(SqlSessionFactoryBean.class).getPropertyDescriptors()).map(PropertyDescriptor::getName)
.collect(Collectors.toSet());
Class<? extends LanguageDriver> defaultLanguageDriver = this.properties.getDefaultScriptingLanguageDriver();
if (factoryPropertyNames.contains("scriptingLanguageDrivers") && !ObjectUtils.isEmpty(this.languageDrivers)) {
// Need to mybatis-spring 2.0.2+
factory.setScriptingLanguageDrivers(this.languageDrivers);
if (defaultLanguageDriver == null && this.languageDrivers.length == 1) {
defaultLanguageDriver = this.languageDrivers[0].getClass();
}
}
if (factoryPropertyNames.contains("defaultScriptingLanguageDriver")) {
// Need to mybatis-spring 2.0.2+
factory.setDefaultScriptingLanguageDriver(defaultLanguageDriver);
}
applySqlSessionFactoryBeanCustomizers(factory);
try {
return factory.getObject();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Bean
@ConditionalOnMissingBean
public SqlSessionFactory sqlSessionFactory() {
return sqlSessionFactories.isEmpty() ? null : sqlSessionFactories.get(0);
} }
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public DataSource dataSource() { public DataSource dataSource() {
return dataSources.isEmpty() ? null : dataSources.get(0);
RoutingDataSource routingDataSource = null;
if (dataSourceProperties != null && !dataSourceProperties.isEmpty()) {
for (String key : dataSourceProperties.keySet()) {
DataSource dataSource = new DataSourceBuilder(dataSourceProperties.get(key)).build();
if (routingDataSource == null) {
routingDataSource = new RoutingDataSource(key, dataSource);
} else {
routingDataSource.addDataSource(key, dataSource);
}
}
}
return routingDataSource;
} }

View File

@ -40,7 +40,6 @@ import org.springframework.boot.autoconfigure.AutoConfigurationPackages;
import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate; import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
@ -81,7 +80,6 @@ import java.util.stream.Stream;
*/ */
@org.springframework.context.annotation.Configuration(proxyBeanMethods = false) @org.springframework.context.annotation.Configuration(proxyBeanMethods = false)
@ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class}) @ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class})
@ConditionalOnProperty("spring.datasource.url")
@ConditionalOnSingleCandidate(DataSource.class) @ConditionalOnSingleCandidate(DataSource.class)
@EnableConfigurationProperties(MybatisFlexProperties.class) @EnableConfigurationProperties(MybatisFlexProperties.class)
@AutoConfigureAfter({DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class}) @AutoConfigureAfter({DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class})

View File

@ -16,7 +16,6 @@
package com.mybatisflex.spring; package com.mybatisflex.spring;
import com.mybatisflex.core.FlexConsts; import com.mybatisflex.core.FlexConsts;
import com.mybatisflex.core.datasource.RoutingDataSource;
import com.mybatisflex.core.mybatis.FlexConfiguration; import com.mybatisflex.core.mybatis.FlexConfiguration;
import com.mybatisflex.core.mybatis.FlexSqlSessionFactoryBuilder; import com.mybatisflex.core.mybatis.FlexSqlSessionFactoryBuilder;
import com.mybatisflex.core.mybatis.FlexXMLConfigBuilder; import com.mybatisflex.core.mybatis.FlexXMLConfigBuilder;
@ -583,8 +582,7 @@ public class FlexSqlSessionFactoryBean extends SqlSessionFactoryBean
} }
targetConfiguration.setEnvironment(new Environment(this.environment, targetConfiguration.setEnvironment(new Environment(this.environment,
this.transactionFactory == null ? new SpringManagedTransactionFactory() : this.transactionFactory, this.transactionFactory == null ? new SpringManagedTransactionFactory() : this.transactionFactory, dataSource));
dataSource instanceof RoutingDataSource ? dataSource : new RoutingDataSource(environment, this.dataSource)));
if (this.mapperLocations != null) { if (this.mapperLocations != null) {
if (this.mapperLocations.length == 0) { if (this.mapperLocations.length == 0) {

View File

@ -44,8 +44,12 @@ public class EntityTestStarter {
// System.out.println(account); // System.out.println(account);
AccountMapper accountMapper = bootstrap.getMapper(AccountMapper.class); AccountMapper accountMapper = bootstrap.getMapper(AccountMapper.class);
for (int i = 0; i < 100; i++) {
Account account = accountMapper.selectOneById(1); Account account = accountMapper.selectOneById(1);
System.out.println(account); }
// System.out.println(account);
// //
// List<Account> allAccount = bootstrap.execute(AccountMapper.class, accountMapper -> // List<Account> allAccount = bootstrap.execute(AccountMapper.class, accountMapper ->
// accountMapper.selectListByQuery(QueryWrapper.create())); // accountMapper.selectListByQuery(QueryWrapper.create()));