add multi datasource; optimize MybatisFlexBootstrap

This commit is contained in:
开源海哥 2023-03-30 22:14:23 +08:00
parent 4023ee1ba5
commit afa5d9a88d
23 changed files with 1390 additions and 614 deletions

View File

@ -0,0 +1,31 @@
/**
* 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.annotation;
import java.lang.annotation.*;
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface UseDataSource {
/**
* 使用哪个数据源配置的是 Mybatis environmentId
*/
String value();
}

View File

@ -19,6 +19,7 @@ package com.mybatisflex.core;
* Mybatis-Flex 可能用到的静态常量
*/
public class FlexConsts {
public static final String NAME = "MyBatis-Flex";
public static final String VERSION = "1.0.5";
public static final String DEFAULT_PRIMARY_FIELD = "id";

View File

@ -15,8 +15,8 @@
*/
package com.mybatisflex.core;
import com.mybatisflex.core.datasource.RoutingDataSource;
import com.mybatisflex.core.dialect.DbType;
import com.mybatisflex.core.dialect.DialectFactory;
import com.mybatisflex.core.mybatis.FlexConfiguration;
import com.mybatisflex.core.mybatis.FlexSqlSessionFactoryBuilder;
import org.apache.ibatis.logging.Log;
@ -57,7 +57,7 @@ public class MybatisFlexBootstrap {
protected final AtomicBoolean started = new AtomicBoolean(false);
protected String environmentId = "mybatis-flex";
protected String environmentId;
protected TransactionFactory transactionFactory;
protected DataSource dataSource;
@ -74,7 +74,8 @@ public class MybatisFlexBootstrap {
* 虽然提供了 getInstance但也允许用户进行实例化
* 用于创建多个 MybatisFlexBootstrap 实例达到管理多数据源的目的
*/
public MybatisFlexBootstrap() {
public MybatisFlexBootstrap(String environmentId) {
this.environmentId = environmentId;
}
private static volatile MybatisFlexBootstrap instance;
@ -83,7 +84,7 @@ public class MybatisFlexBootstrap {
if (instance == null) {
synchronized (MybatisFlexBootstrap.class) {
if (instance == null) {
instance = new MybatisFlexBootstrap();
instance = new MybatisFlexBootstrap(FlexConsts.NAME);
}
}
}
@ -113,7 +114,7 @@ public class MybatisFlexBootstrap {
transactionFactory = new JdbcTransactionFactory();
}
Environment environment = new Environment(environmentId, transactionFactory, dataSource);
Environment environment = new Environment(environmentId, transactionFactory, new RoutingDataSource(environmentId, dataSource));
configuration = new FlexConfiguration(environment);
}
@ -142,11 +143,8 @@ public class MybatisFlexBootstrap {
@Deprecated
public <R, T> R execute(Class<T> mapperClass, Function<T, R> function) {
try (SqlSession sqlSession = openSession()) {
DialectFactory.setHintDbType(dbType);
T mapper = sqlSession.getMapper(mapperClass);
return function.apply(mapper);
} finally {
DialectFactory.clearHintDbType();
}
}
@ -162,20 +160,18 @@ public class MybatisFlexBootstrap {
/**
* 直接获取 mapper 对象执行
*
* @param mapperClass
* @return mapperObject
*/
public <T> T getMapper(Class<T> mapperClass) {
Object mapperObject = MapUtil.computeIfAbsent(mapperObjects, mapperClass, clazz ->
Proxy.newProxyInstance(MybatisFlexBootstrap.class.getClassLoader()
Proxy.newProxyInstance(mapperClass.getClassLoader()
, new Class[]{mapperClass}
, (proxy, method, args) -> {
try (SqlSession sqlSession = openSession()) {
DialectFactory.setHintDbType(dbType);
T mapper1 = sqlSession.getMapper(mapperClass);
return method.invoke(mapper1, args);
} finally {
DialectFactory.clearHintDbType();
}
}));
return (T) mapperObject;
@ -210,7 +206,6 @@ public class MybatisFlexBootstrap {
}
public String getEnvironmentId() {
return environmentId;
}

View File

@ -0,0 +1,84 @@
/**
* 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.core.datasource;
import javax.sql.DataSource;
import java.io.PrintWriter;
import java.sql.SQLException;
import java.util.logging.Logger;
public abstract class AbstractDataSource implements DataSource {
/**
* Returns 0, indicating the default system timeout is to be used.
*/
@Override
public int getLoginTimeout() throws SQLException {
return 0;
}
/**
* Setting a login timeout is not supported.
*/
@Override
public void setLoginTimeout(int timeout) throws SQLException {
throw new UnsupportedOperationException("setLoginTimeout");
}
/**
* LogWriter methods are not supported.
*/
@Override
public PrintWriter getLogWriter() {
throw new UnsupportedOperationException("getLogWriter");
}
/**
* LogWriter methods are not supported.
*/
@Override
public void setLogWriter(PrintWriter pw) throws SQLException {
throw new UnsupportedOperationException("setLogWriter");
}
@Override
@SuppressWarnings("unchecked")
public <T> T unwrap(Class<T> iface) throws SQLException {
if (iface.isInstance(this)) {
return (T) this;
}
throw new SQLException("DataSource of type [" + getClass().getName() +
"] cannot be unwrapped as [" + iface.getName() + "]");
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return iface.isInstance(this);
}
@Override
public Logger getParentLogger() {
return Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
}
}

View File

@ -0,0 +1,34 @@
/**
* 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.core.datasource;
public class DataSourceKey {
private static ThreadLocal<String> keyThreadLocal = new ThreadLocal<>();
public static void use(String environmentId){
keyThreadLocal.set(environmentId);
}
public static void clear(){
keyThreadLocal.remove();
}
public static String get(){
return keyThreadLocal.get();
}
}

View File

@ -0,0 +1,65 @@
/**
* 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.core.datasource;
import com.mybatisflex.core.util.StringUtil;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
public class RoutingDataSource extends AbstractDataSource {
private static Map<String, DataSource> dataSourceMap = new HashMap<>();
private DataSource delegate;
public RoutingDataSource(String environmentId, DataSource delegate) {
this.delegate = delegate;
dataSourceMap.put(environmentId, delegate);
}
@Override
public Connection getConnection() throws SQLException {
return getDataSource().getConnection();
}
@Override
public Connection getConnection(String username, String password) throws SQLException {
return getDataSource().getConnection(username, password);
}
private DataSource getDataSource() {
DataSource dataSource = delegate;
if (dataSourceMap.size() > 1) {
String environmentId = DataSourceKey.get();
if (StringUtil.isNotBlank(environmentId)) {
dataSource = dataSourceMap.get(environmentId);
if (dataSource == null) {
throw new IllegalStateException("Cannot get target DataSource for environmentId [" + environmentId + "]");
}
}
}
return dataSource;
}
public DataSource getDelegate() {
return delegate;
}
}

View File

@ -38,7 +38,9 @@ import org.apache.ibatis.mapping.ResultMap;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.session.SqlSession;
import java.lang.reflect.Proxy;
import java.util.Map;
public class FlexConfiguration extends Configuration {
@ -237,6 +239,7 @@ public class FlexConfiguration extends Configuration {
.build();
}
private TableInfo getTableInfo(MappedStatement ms) {
String mapperClassName = ms.getId().substring(0, ms.getId().lastIndexOf("."));
try {
@ -248,4 +251,12 @@ public class FlexConfiguration extends Configuration {
}
@Override
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
T mapper = super.getMapper(type, sqlSession);
return (T) Proxy.newProxyInstance(type.getClassLoader()
, new Class[]{type}
, new MapperInvocationHandler(mapper, this));
}
}

View File

@ -31,6 +31,8 @@ import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
public class FlexSqlSessionFactoryBuilder extends SqlSessionFactoryBuilder {
@ -94,17 +96,14 @@ public class FlexSqlSessionFactoryBuilder extends SqlSessionFactoryBuilder {
*/
private DbType getDbType(Configuration configuration) {
DataSource dataSource = configuration.getEnvironment().getDataSource();
String jdbcUrl = getJdbcUrl(dataSource);
if (StringUtil.isNotBlank(jdbcUrl)) {
return DbTypeUtil.parseDbType(jdbcUrl);
}
if ("org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseFactory$EmbeddedDataSourceProxy"
.equals(dataSource.getClass().getName())){
return DbType.H2;
}
return null;
throw new IllegalStateException("Cannot get dataSource jdbcUrl: " + dataSource.getClass().getName());
}
/**
@ -125,6 +124,22 @@ public class FlexSqlSessionFactoryBuilder extends SqlSessionFactoryBuilder {
//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

@ -0,0 +1,85 @@
/**
* 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.core.mybatis;
import com.mybatisflex.annotation.UseDataSource;
import com.mybatisflex.core.FlexGlobalConfig;
import com.mybatisflex.core.datasource.DataSourceKey;
import com.mybatisflex.core.dialect.DbType;
import com.mybatisflex.core.dialect.DialectFactory;
import com.mybatisflex.core.util.StringUtil;
import org.apache.ibatis.session.Configuration;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MapperInvocationHandler implements InvocationHandler {
private Object mapper;
private Configuration configuration;
public MapperInvocationHandler(Object mapper, Configuration configuration) {
this.mapper = mapper;
this.configuration = configuration;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
boolean clearDsKey = false;
boolean clearDbType = false;
try {
//获取用户动态指定由用户指定数据源则应该有用户清除
String environmentId = DataSourceKey.get();
//通过 方法 的注解去获取
if (StringUtil.isBlank(environmentId)) {
environmentId = getMethodEnvironmentId(method);
if (StringUtil.isNotBlank(environmentId)) {
DataSourceKey.use(environmentId);
clearDsKey = true;
}
}
if (StringUtil.isBlank(environmentId)) {
environmentId = configuration.getEnvironment().getId();
}
//优先获取用户自己配置的 dbType
DbType dbType = DialectFactory.getHintDbType();
if (dbType == null) {
dbType = FlexGlobalConfig.getConfig(environmentId).getDbType();
DialectFactory.setHintDbType(dbType);
clearDbType = true;
}
return method.invoke(mapper, args);
} finally {
if (clearDbType) {
DialectFactory.clearHintDbType();
}
if (clearDsKey) {
DataSourceKey.clear();
}
}
}
private String getMethodEnvironmentId(Method method) {
UseDataSource useDataSource = method.getAnnotation(UseDataSource.class);
return useDataSource != null ? useDataSource.value() : null;
}
}

View File

@ -17,7 +17,6 @@ package com.mybatisflex.core.row;
import com.mybatisflex.core.FlexGlobalConfig;
import com.mybatisflex.core.dialect.DbType;
import com.mybatisflex.core.dialect.DialectFactory;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import org.apache.ibatis.executor.BatchResult;
@ -52,11 +51,9 @@ public class RowMapperInvoker {
private <R> R execute(Function<RowMapper, R> function) {
SqlSession sqlSession = rowSessionManager.getSqlSession(sqlSessionFactory);
try {
DialectFactory.setHintDbType(dbType);
RowMapper mapper = sqlSession.getMapper(RowMapper.class);
return function.apply(mapper);
} finally {
DialectFactory.clearHintDbType();
rowSessionManager.releaseSqlSession(sqlSession, sqlSessionFactory);
}
}
@ -75,7 +72,6 @@ public class RowMapperInvoker {
int[] results = new int[rows.size()];
SqlSession sqlSession = rowSessionManager.getSqlSession(sqlSessionFactory, ExecutorType.BATCH);
try {
DialectFactory.setHintDbType(dbType);
RowMapper mapper = sqlSession.getMapper(RowMapper.class);
int counter = 0;
int resultsPos = 0;
@ -94,7 +90,6 @@ public class RowMapperInvoker {
}
}
} finally {
DialectFactory.clearHintDbType();
rowSessionManager.releaseSqlSession(sqlSession, sqlSessionFactory);
}
return results;

View File

@ -47,6 +47,39 @@
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>4.0.3</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.16</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.github.chris2018998</groupId>
<artifactId>beecp</artifactId>
<version>3.4.0</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
<version>2.9.0</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
</dependencies>
<build>

View File

@ -0,0 +1,50 @@
/**
* 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 org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.context.annotation.Conditional;
import org.springframework.core.type.AnnotatedTypeMetadata;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.Map;
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Conditional(ConditionalOnPropertyEmpty.OnPropertyNotEmptyCondition.class)
public @interface ConditionalOnPropertyEmpty {
String value();
class OnPropertyNotEmptyCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Map<String, Object> attrs = metadata.getAnnotationAttributes(ConditionalOnPropertyEmpty.class.getName());
if (attrs == null) {
return false;
}
String propertyName = (String) attrs.get("value");
String val = context.getEnvironment().getProperty(propertyName);
return val == null || val.trim().length() == 0;
}
}
}

View File

@ -0,0 +1,70 @@
/**
* 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

@ -0,0 +1,278 @@
/**
* 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 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;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.type.TypeHandler;
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.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
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 java.beans.PropertyDescriptor;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* 多数据源的配置支持
*/
@org.springframework.context.annotation.Configuration(proxyBeanMethods = false)
@ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class})
@ConditionalOnPropertyEmpty("spring.datasource.url")
@EnableConfigurationProperties(MybatisFlexProperties.class)
@AutoConfigureAfter({MybatisLanguageDriverAutoConfiguration.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
, 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, DataSourceProperty> datasourceMap) {
datasourceMap.forEach((s, dsp) -> {
SqlSessionFactory sqlSessionFactory = buildSqlSessionFactory(s, createDataSource(dsp));
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;
}
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.get(0);
}
@Bean
@ConditionalOnMissingBean
public DataSource dataSource() {
return dataSources.get(0);
}
}

View File

@ -40,8 +40,8 @@ import org.springframework.boot.autoconfigure.AutoConfigurationPackages;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
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.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Bean;
@ -80,28 +80,30 @@ import java.util.stream.Stream;
*/
@org.springframework.context.annotation.Configuration(proxyBeanMethods = false)
@ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class})
@ConditionalOnProperty("spring.datasource.url")
@ConditionalOnSingleCandidate(DataSource.class)
@EnableConfigurationProperties(MybatisFlexProperties.class)
@AutoConfigureAfter({DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class})
//@AutoConfigureAfter({DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class})
@AutoConfigureAfter({ MybatisLanguageDriverAutoConfiguration.class})
public class MybatisFlexAutoConfiguration implements InitializingBean {
private static final Logger logger = LoggerFactory.getLogger(MybatisFlexAutoConfiguration.class);
protected static final Logger logger = LoggerFactory.getLogger(MybatisFlexAutoConfiguration.class);
private final MybatisFlexProperties properties;
protected final MybatisFlexProperties properties;
private final Interceptor[] interceptors;
protected final Interceptor[] interceptors;
private final TypeHandler[] typeHandlers;
protected final TypeHandler[] typeHandlers;
private final LanguageDriver[] languageDrivers;
protected final LanguageDriver[] languageDrivers;
private final ResourceLoader resourceLoader;
protected final ResourceLoader resourceLoader;
private final DatabaseIdProvider databaseIdProvider;
protected final DatabaseIdProvider databaseIdProvider;
private final List<ConfigurationCustomizer> configurationCustomizers;
protected final List<ConfigurationCustomizer> configurationCustomizers;
private final List<SqlSessionFactoryBeanCustomizer> sqlSessionFactoryBeanCustomizers;
protected final List<SqlSessionFactoryBeanCustomizer> sqlSessionFactoryBeanCustomizers;
public MybatisFlexAutoConfiguration(MybatisFlexProperties properties, ObjectProvider<Interceptor[]> interceptorsProvider,
ObjectProvider<TypeHandler[]> typeHandlersProvider, ObjectProvider<LanguageDriver[]> languageDriversProvider,
@ -188,7 +190,7 @@ public class MybatisFlexAutoConfiguration implements InitializingBean {
return factory.getObject();
}
private void applyConfiguration(SqlSessionFactoryBean factory) {
protected void applyConfiguration(SqlSessionFactoryBean factory) {
MybatisFlexProperties.CoreConfiguration coreConfiguration = this.properties.getConfiguration();
// Configuration configuration = null;
FlexConfiguration configuration = null;
@ -206,7 +208,7 @@ public class MybatisFlexAutoConfiguration implements InitializingBean {
factory.setConfiguration(configuration);
}
private void applySqlSessionFactoryBeanCustomizers(SqlSessionFactoryBean factory) {
protected void applySqlSessionFactoryBeanCustomizers(SqlSessionFactoryBean factory) {
if (!CollectionUtils.isEmpty(this.sqlSessionFactoryBeanCustomizers)) {
for (SqlSessionFactoryBeanCustomizer customizer : this.sqlSessionFactoryBeanCustomizers) {
customizer.customize(factory);

View File

@ -29,6 +29,7 @@ import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import java.io.IOException;
import java.util.Map;
import java.util.Optional;
import java.util.Properties;
import java.util.Set;
@ -44,6 +45,11 @@ public class MybatisFlexProperties {
private static final ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();
//多数据源的配置
//mybatis-flex.datasource.ds1.url=***
//mybatis-flex.datasource.ds2.url=***
private Map<String, DataSourceProperty> datasource;
/**
* Location of MyBatis xml config file.
*/
@ -96,6 +102,14 @@ public class MybatisFlexProperties {
*/
private CoreConfiguration configuration;
public Map<String, DataSourceProperty> getDatasource() {
return datasource;
}
public void setDatasource(Map<String, DataSourceProperty> datasource) {
this.datasource = datasource;
}
/**
* @since 1.1.0
*/

View File

@ -25,6 +25,12 @@
"reason": "The 'userdirective' is deprecated since Velocity 2.x. This property defined for keeping backward compatibility with older velocity version.",
"replacement": "mybatis-flex.scripting-language-driver.velocity.velocity-settings.runtime.custom_directives"
}
},
{
"defaultValue": true,
"name": "mybatis-flex.datasource",
"description": "多数据源配置",
"type": "java.util.Map<java.lang.String,com.mybatisflex.spring.boot.DataSourceProperty>"
}
]
}

View File

@ -3,5 +3,6 @@ org.springframework.boot.sql.init.dependency.DependsOnDatabaseInitializationDete
com.mybatisflex.spring.boot.MybatisFlexDependsOnDatabaseInitializationDetector
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.mybatisflex.spring.boot.DbAutoConfiguration,\
com.mybatisflex.spring.boot.MultiDataSourceAutoConfiguration,\
com.mybatisflex.spring.boot.MybatisFlexAutoConfiguration,\
com.mybatisflex.spring.boot.MybatisLanguageDriverAutoConfiguration

View File

@ -1,2 +1,4 @@
com.mybatisflex.spring.boot.DbAutoConfiguration
com.mybatisflex.spring.boot.MultiDataSourceAutoConfiguration
com.mybatisflex.spring.boot.MybatisFlexAutoConfiguration
com.mybatisflex.spring.boot.MybatisLanguageDriverAutoConfiguration

View File

@ -15,6 +15,8 @@
*/
package com.mybatisflex.spring;
import com.mybatisflex.core.FlexConsts;
import com.mybatisflex.core.datasource.RoutingDataSource;
import com.mybatisflex.core.mybatis.FlexConfiguration;
import com.mybatisflex.core.mybatis.FlexSqlSessionFactoryBuilder;
import com.mybatisflex.core.mybatis.FlexXMLConfigBuilder;
@ -101,7 +103,8 @@ public class FlexSqlSessionFactoryBean extends SqlSessionFactoryBean
private SqlSessionFactory sqlSessionFactory;
// EnvironmentAware requires spring 3.1
private String environment = SqlSessionFactoryBean.class.getSimpleName();
// private String environment = SqlSessionFactoryBean.class.getSimpleName();
private String environment = FlexConsts.NAME;
private boolean failFast;
@ -581,7 +584,7 @@ public class FlexSqlSessionFactoryBean extends SqlSessionFactoryBean
targetConfiguration.setEnvironment(new Environment(this.environment,
this.transactionFactory == null ? new SpringManagedTransactionFactory() : this.transactionFactory,
this.dataSource));
dataSource instanceof RoutingDataSource ? dataSource : new RoutingDataSource(environment, this.dataSource)));
if (this.mapperLocations != null) {
if (this.mapperLocations.length == 0) {

View File

@ -31,7 +31,7 @@ public class EntityTestStarter {
.addScript("data.sql")
.build();
MybatisFlexBootstrap bootstrap = new MybatisFlexBootstrap()
MybatisFlexBootstrap bootstrap = MybatisFlexBootstrap.getInstance()
.setDataSource(dataSource)
.setLogImpl(StdOutImpl.class)
.addMapper(AccountMapper.class)

View File

@ -35,7 +35,7 @@ public class RowTestStarter {
.addScript("data.sql")
.build();
MybatisFlexBootstrap bootstrap = new MybatisFlexBootstrap()
MybatisFlexBootstrap bootstrap = MybatisFlexBootstrap.getInstance()
.setDataSource(dataSource)
.setLogImpl(StdOutImpl.class)
.start();

View File

@ -69,6 +69,7 @@
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>