diff --git a/mybatis-flex-annotation/src/main/java/com/mybatisflex/annotation/UseDataSource.java b/mybatis-flex-annotation/src/main/java/com/mybatisflex/annotation/UseDataSource.java new file mode 100644 index 00000000..b4386fe2 --- /dev/null +++ b/mybatis-flex-annotation/src/main/java/com/mybatisflex/annotation/UseDataSource.java @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com). + *

+ * 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 + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * 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(); + + +} \ No newline at end of file diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/FlexConsts.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/FlexConsts.java index ddec3b00..ccf318e0 100644 --- a/mybatis-flex-core/src/main/java/com/mybatisflex/core/FlexConsts.java +++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/FlexConsts.java @@ -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"; diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/MybatisFlexBootstrap.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/MybatisFlexBootstrap.java index c48d1ed6..e59c64d8 100644 --- a/mybatis-flex-core/src/main/java/com/mybatisflex/core/MybatisFlexBootstrap.java +++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/MybatisFlexBootstrap.java @@ -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 execute(Class mapperClass, Function 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 getMapper(Class 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; } diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/datasource/AbstractDataSource.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/datasource/AbstractDataSource.java new file mode 100644 index 00000000..6fcb3ac0 --- /dev/null +++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/datasource/AbstractDataSource.java @@ -0,0 +1,84 @@ +/** + * Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com). + *

+ * 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 + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * 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 unwrap(Class 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); + } + +} diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/datasource/DataSourceKey.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/datasource/DataSourceKey.java new file mode 100644 index 00000000..9c5bbefe --- /dev/null +++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/datasource/DataSourceKey.java @@ -0,0 +1,34 @@ +/** + * Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com). + *

+ * 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 + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * 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 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(); + } + +} diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/datasource/RoutingDataSource.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/datasource/RoutingDataSource.java new file mode 100644 index 00000000..1e5d1d2b --- /dev/null +++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/datasource/RoutingDataSource.java @@ -0,0 +1,65 @@ +/** + * Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com). + *

+ * 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 + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * 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 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; + } + +} diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/mybatis/FlexConfiguration.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/mybatis/FlexConfiguration.java index 1ea6489e..a3f01593 100644 --- a/mybatis-flex-core/src/main/java/com/mybatisflex/core/mybatis/FlexConfiguration.java +++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/mybatis/FlexConfiguration.java @@ -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 getMapper(Class type, SqlSession sqlSession) { + T mapper = super.getMapper(type, sqlSession); + return (T) Proxy.newProxyInstance(type.getClassLoader() + , new Class[]{type} + , new MapperInvocationHandler(mapper, this)); + + } } diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/mybatis/FlexSqlSessionFactoryBuilder.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/mybatis/FlexSqlSessionFactoryBuilder.java index abe8ed60..46376b2d 100644 --- a/mybatis-flex-core/src/main/java/com/mybatisflex/core/mybatis/FlexSqlSessionFactoryBuilder.java +++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/mybatis/FlexSqlSessionFactoryBuilder.java @@ -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)){ + + 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; } } diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/mybatis/MapperInvocationHandler.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/mybatis/MapperInvocationHandler.java new file mode 100644 index 00000000..13a129d9 --- /dev/null +++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/mybatis/MapperInvocationHandler.java @@ -0,0 +1,85 @@ +/** + * Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com). + *

+ * 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 + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * 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; + } + +} diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/row/RowMapperInvoker.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/row/RowMapperInvoker.java index d5637ec9..d1e7c71c 100644 --- a/mybatis-flex-core/src/main/java/com/mybatisflex/core/row/RowMapperInvoker.java +++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/row/RowMapperInvoker.java @@ -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 execute(Function 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; diff --git a/mybatis-flex-spring-boot-starter/pom.xml b/mybatis-flex-spring-boot-starter/pom.xml index a64e1b8d..274d2e86 100644 --- a/mybatis-flex-spring-boot-starter/pom.xml +++ b/mybatis-flex-spring-boot-starter/pom.xml @@ -47,6 +47,39 @@ true + + com.zaxxer + HikariCP + 4.0.3 + compile + true + + + + com.alibaba + druid + 1.2.16 + compile + true + + + + com.github.chris2018998 + beecp + 3.4.0 + compile + true + + + + org.apache.commons + commons-dbcp2 + 2.9.0 + compile + true + + + diff --git a/mybatis-flex-spring-boot-starter/src/main/java/com/mybatisflex/spring/boot/ConditionalOnPropertyEmpty.java b/mybatis-flex-spring-boot-starter/src/main/java/com/mybatisflex/spring/boot/ConditionalOnPropertyEmpty.java new file mode 100644 index 00000000..4f274e29 --- /dev/null +++ b/mybatis-flex-spring-boot-starter/src/main/java/com/mybatisflex/spring/boot/ConditionalOnPropertyEmpty.java @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com). + *

+ * 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 + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * 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 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; + } + } +} \ No newline at end of file diff --git a/mybatis-flex-spring-boot-starter/src/main/java/com/mybatisflex/spring/boot/DataSourceProperty.java b/mybatis-flex-spring-boot-starter/src/main/java/com/mybatisflex/spring/boot/DataSourceProperty.java new file mode 100644 index 00000000..bea74a3b --- /dev/null +++ b/mybatis-flex-spring-boot-starter/src/main/java/com/mybatisflex/spring/boot/DataSourceProperty.java @@ -0,0 +1,70 @@ +/** + * Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com). + *

+ * 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 + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * 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; + } +} diff --git a/mybatis-flex-spring-boot-starter/src/main/java/com/mybatisflex/spring/boot/MultiDataSourceAutoConfiguration.java b/mybatis-flex-spring-boot-starter/src/main/java/com/mybatisflex/spring/boot/MultiDataSourceAutoConfiguration.java new file mode 100644 index 00000000..6b6c291b --- /dev/null +++ b/mybatis-flex-spring-boot-starter/src/main/java/com/mybatisflex/spring/boot/MultiDataSourceAutoConfiguration.java @@ -0,0 +1,278 @@ +/** + * Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com). + *

+ * 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 + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * 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 sqlSessionFactories = new ArrayList<>(); + private List dataSources = new ArrayList<>(); + + + private static Map 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 interceptorsProvider + , ObjectProvider typeHandlersProvider + , ObjectProvider languageDriversProvider + , ResourceLoader resourceLoader + , ObjectProvider databaseIdProvider + , ObjectProvider> configurationCustomizersProvider + , ObjectProvider> sqlSessionFactoryBeanCustomizers) { + super(properties, interceptorsProvider, typeHandlersProvider, languageDriversProvider, resourceLoader, databaseIdProvider, configurationCustomizersProvider, sqlSessionFactoryBeanCustomizers); + + initDataSources(properties.getDatasource()); + } + + + private void initDataSources(Map 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 factoryPropertyNames = Stream + .of(new BeanWrapperImpl(SqlSessionFactoryBean.class).getPropertyDescriptors()).map(PropertyDescriptor::getName) + .collect(Collectors.toSet()); + Class 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); + } + + +} diff --git a/mybatis-flex-spring-boot-starter/src/main/java/com/mybatisflex/spring/boot/MybatisFlexAutoConfiguration.java b/mybatis-flex-spring-boot-starter/src/main/java/com/mybatisflex/spring/boot/MybatisFlexAutoConfiguration.java index 407283a6..0e36bae8 100644 --- a/mybatis-flex-spring-boot-starter/src/main/java/com/mybatisflex/spring/boot/MybatisFlexAutoConfiguration.java +++ b/mybatis-flex-spring-boot-starter/src/main/java/com/mybatisflex/spring/boot/MybatisFlexAutoConfiguration.java @@ -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 configurationCustomizers; + protected final List configurationCustomizers; - private final List sqlSessionFactoryBeanCustomizers; + protected final List sqlSessionFactoryBeanCustomizers; public MybatisFlexAutoConfiguration(MybatisFlexProperties properties, ObjectProvider interceptorsProvider, ObjectProvider typeHandlersProvider, ObjectProvider 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); diff --git a/mybatis-flex-spring-boot-starter/src/main/java/com/mybatisflex/spring/boot/MybatisFlexProperties.java b/mybatis-flex-spring-boot-starter/src/main/java/com/mybatisflex/spring/boot/MybatisFlexProperties.java index aab26d17..97cd24bd 100644 --- a/mybatis-flex-spring-boot-starter/src/main/java/com/mybatisflex/spring/boot/MybatisFlexProperties.java +++ b/mybatis-flex-spring-boot-starter/src/main/java/com/mybatisflex/spring/boot/MybatisFlexProperties.java @@ -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; @@ -42,649 +43,662 @@ import java.util.stream.Stream; public class MybatisFlexProperties { - private static final ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver(); + private static final ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver(); - /** - * Location of MyBatis xml config file. - */ - private String configLocation; - - /** - * Locations of MyBatis mapper files. - */ - private String[] mapperLocations; - - /** - * Packages to search type aliases. (Package delimiters are ",; \t\n") - */ - private String typeAliasesPackage; - - /** - * The super class for filtering type alias. If this not specifies, the MyBatis deal as type alias all classes that - * searched from typeAliasesPackage. - */ - private Class typeAliasesSuperType; - - /** - * Packages to search for type handlers. (Package delimiters are ",; \t\n") - */ - private String typeHandlersPackage; - - /** - * Indicates whether perform presence check of the MyBatis xml config file. - */ - private boolean checkConfigLocation = false; - - /** - * Execution mode for {@link org.mybatis.spring.SqlSessionTemplate}. - */ - private ExecutorType executorType; - - /** - * The default scripting language driver class. (Available when use together with mybatis-spring 2.0.2+) - */ - private Class defaultScriptingLanguageDriver; - - /** - * Externalized properties for MyBatis configuration. - */ - private Properties configurationProperties; - - /** - * A Configuration object for customize default settings. If {@link #configLocation} is specified, this property is - * not used. - */ - private CoreConfiguration configuration; - - /** - * @since 1.1.0 - */ - public String getConfigLocation() { - return this.configLocation; - } - - /** - * @since 1.1.0 - */ - public void setConfigLocation(String configLocation) { - this.configLocation = configLocation; - } - - public String[] getMapperLocations() { - return this.mapperLocations; - } - - public void setMapperLocations(String[] mapperLocations) { - this.mapperLocations = mapperLocations; - } - - public String getTypeHandlersPackage() { - return this.typeHandlersPackage; - } - - public void setTypeHandlersPackage(String typeHandlersPackage) { - this.typeHandlersPackage = typeHandlersPackage; - } - - public String getTypeAliasesPackage() { - return this.typeAliasesPackage; - } - - public void setTypeAliasesPackage(String typeAliasesPackage) { - this.typeAliasesPackage = typeAliasesPackage; - } - - /** - * @since 1.3.3 - */ - public Class getTypeAliasesSuperType() { - return typeAliasesSuperType; - } - - /** - * @since 1.3.3 - */ - public void setTypeAliasesSuperType(Class typeAliasesSuperType) { - this.typeAliasesSuperType = typeAliasesSuperType; - } - - public boolean isCheckConfigLocation() { - return this.checkConfigLocation; - } - - public void setCheckConfigLocation(boolean checkConfigLocation) { - this.checkConfigLocation = checkConfigLocation; - } - - public ExecutorType getExecutorType() { - return this.executorType; - } - - public void setExecutorType(ExecutorType executorType) { - this.executorType = executorType; - } - - /** - * @since 2.1.0 - */ - public Class getDefaultScriptingLanguageDriver() { - return defaultScriptingLanguageDriver; - } - - /** - * @since 2.1.0 - */ - public void setDefaultScriptingLanguageDriver(Class defaultScriptingLanguageDriver) { - this.defaultScriptingLanguageDriver = defaultScriptingLanguageDriver; - } - - /** - * @since 1.2.0 - */ - public Properties getConfigurationProperties() { - return configurationProperties; - } - - /** - * @since 1.2.0 - */ - public void setConfigurationProperties(Properties configurationProperties) { - this.configurationProperties = configurationProperties; - } - - public CoreConfiguration getConfiguration() { - return configuration; - } - - public void setConfiguration(CoreConfiguration configuration) { - this.configuration = configuration; - } - - public Resource[] resolveMapperLocations() { - return Stream.of(Optional.ofNullable(this.mapperLocations).orElse(new String[0])) - .flatMap(location -> Stream.of(getResources(location))).toArray(Resource[]::new); - } - - private Resource[] getResources(String location) { - try { - return resourceResolver.getResources(location); - } catch (IOException e) { - return new Resource[0]; - } - } - - /** - * The configuration properties for mybatis core module. - * - * @since 3.0.0 - */ - public static class CoreConfiguration { + //多数据源的配置 + //mybatis-flex.datasource.ds1.url=*** + //mybatis-flex.datasource.ds2.url=*** + private Map datasource; /** - * Allows using RowBounds on nested statements. If allow, set the false. Default is false. + * Location of MyBatis xml config file. */ - private Boolean safeRowBoundsEnabled; + private String configLocation; /** - * Allows using ResultHandler on nested statements. If allow, set the false. Default is true. + * Locations of MyBatis mapper files. */ - private Boolean safeResultHandlerEnabled; + private String[] mapperLocations; /** - * Enables automatic mapping from classic database column names A_COLUMN to camel case classic Java property names - * aColumn. Default is true. + * Packages to search type aliases. (Package delimiters are ",; \t\n") */ - private Boolean mapUnderscoreToCamelCase = true; + private String typeAliasesPackage; /** - * When enabled, any method call will load all the lazy properties of the object. Otherwise, each property is loaded - * on demand (see also lazyLoadTriggerMethods). Default is false. + * The super class for filtering type alias. If this not specifies, the MyBatis deal as type alias all classes that + * searched from typeAliasesPackage. */ - private Boolean aggressiveLazyLoading; + private Class typeAliasesSuperType; /** - * Allows or disallows multiple ResultSets to be returned from a single statement (compatible driver required). - * Default is true. + * Packages to search for type handlers. (Package delimiters are ",; \t\n") */ - private Boolean multipleResultSetsEnabled; + private String typeHandlersPackage; /** - * Allows JDBC support for generated keys. A compatible driver is required. This setting forces generated keys to be - * used if set to true, as some drivers deny compatibility but still work (e.g. Derby). Default is false. + * Indicates whether perform presence check of the MyBatis xml config file. */ - private Boolean useGeneratedKeys; + private boolean checkConfigLocation = false; /** - * Uses the column label instead of the column name. Different drivers behave differently in this respect. Refer to - * the driver documentation, or test out both modes to determine how your driver behaves. Default is true. + * Execution mode for {@link org.mybatis.spring.SqlSessionTemplate}. */ - private Boolean useColumnLabel; + private ExecutorType executorType; /** - * Globally enables or disables any caches configured in any mapper under this configuration. Default is true. + * The default scripting language driver class. (Available when use together with mybatis-spring 2.0.2+) */ - private Boolean cacheEnabled; + private Class defaultScriptingLanguageDriver; /** - * Specifies if setters or map's put method will be called when a retrieved value is null. It is useful when you - * rely on Map.keySet() or null value initialization. Note primitives such as (int,boolean,etc.) will not be set to - * null. Default is false. + * Externalized properties for MyBatis configuration. */ - private Boolean callSettersOnNulls; + private Properties configurationProperties; /** - * Allow referencing statement parameters by their actual names declared in the method signature. To use this - * feature, your project must be compiled in Java 8 with -parameters option. Default is true. + * A Configuration object for customize default settings. If {@link #configLocation} is specified, this property is + * not used. */ - private Boolean useActualParamName; + private CoreConfiguration configuration; + + public Map getDatasource() { + return datasource; + } + + public void setDatasource(Map datasource) { + this.datasource = datasource; + } /** - * MyBatis, by default, returns null when all the columns of a returned row are NULL. When this setting is enabled, - * MyBatis returns an empty instance instead. Note that it is also applied to nested results (i.e. collectioin and - * association). Default is false. + * @since 1.1.0 */ - private Boolean returnInstanceForEmptyRow; + public String getConfigLocation() { + return this.configLocation; + } /** - * Removes extra whitespace characters from the SQL. Note that this also affects literal strings in SQL. Default is - * false. + * @since 1.1.0 */ - private Boolean shrinkWhitespacesInSql; + public void setConfigLocation(String configLocation) { + this.configLocation = configLocation; + } + + public String[] getMapperLocations() { + return this.mapperLocations; + } + + public void setMapperLocations(String[] mapperLocations) { + this.mapperLocations = mapperLocations; + } + + public String getTypeHandlersPackage() { + return this.typeHandlersPackage; + } + + public void setTypeHandlersPackage(String typeHandlersPackage) { + this.typeHandlersPackage = typeHandlersPackage; + } + + public String getTypeAliasesPackage() { + return this.typeAliasesPackage; + } + + public void setTypeAliasesPackage(String typeAliasesPackage) { + this.typeAliasesPackage = typeAliasesPackage; + } /** - * Specifies the default value of 'nullable' attribute on 'foreach' tag. Default is false. + * @since 1.3.3 */ - private Boolean nullableOnForEach; + public Class getTypeAliasesSuperType() { + return typeAliasesSuperType; + } /** - * When applying constructor auto-mapping, argument name is used to search the column to map instead of relying on - * the column order. Default is false. + * @since 1.3.3 */ - private Boolean argNameBasedConstructorAutoMapping; + public void setTypeAliasesSuperType(Class typeAliasesSuperType) { + this.typeAliasesSuperType = typeAliasesSuperType; + } + + public boolean isCheckConfigLocation() { + return this.checkConfigLocation; + } + + public void setCheckConfigLocation(boolean checkConfigLocation) { + this.checkConfigLocation = checkConfigLocation; + } + + public ExecutorType getExecutorType() { + return this.executorType; + } + + public void setExecutorType(ExecutorType executorType) { + this.executorType = executorType; + } /** - * Globally enables or disables lazy loading. When enabled, all relations will be lazily loaded. This value can be - * superseded for a specific relation by using the fetchType attribute on it. Default is False. + * @since 2.1.0 */ - private Boolean lazyLoadingEnabled; + public Class getDefaultScriptingLanguageDriver() { + return defaultScriptingLanguageDriver; + } /** - * Sets the number of seconds the driver will wait for a response from the database. + * @since 2.1.0 */ - private Integer defaultStatementTimeout; + public void setDefaultScriptingLanguageDriver(Class defaultScriptingLanguageDriver) { + this.defaultScriptingLanguageDriver = defaultScriptingLanguageDriver; + } /** - * Sets the driver a hint as to control fetching size for return results. This parameter value can be override by a - * query setting. + * @since 1.2.0 */ - private Integer defaultFetchSize; + public Properties getConfigurationProperties() { + return configurationProperties; + } /** - * MyBatis uses local cache to prevent circular references and speed up repeated nested queries. By default - * (SESSION) all queries executed during a session are cached. If localCacheScope=STATEMENT local session will be - * used just for statement execution, no data will be shared between two different calls to the same SqlSession. - * Default is SESSION. + * @since 1.2.0 */ - private LocalCacheScope localCacheScope; + public void setConfigurationProperties(Properties configurationProperties) { + this.configurationProperties = configurationProperties; + } + + public CoreConfiguration getConfiguration() { + return configuration; + } + + public void setConfiguration(CoreConfiguration configuration) { + this.configuration = configuration; + } + + public Resource[] resolveMapperLocations() { + return Stream.of(Optional.ofNullable(this.mapperLocations).orElse(new String[0])) + .flatMap(location -> Stream.of(getResources(location))).toArray(Resource[]::new); + } + + private Resource[] getResources(String location) { + try { + return resourceResolver.getResources(location); + } catch (IOException e) { + return new Resource[0]; + } + } /** - * Specifies the JDBC type for null values when no specific JDBC type was provided for the parameter. Some drivers - * require specifying the column JDBC type but others work with generic values like NULL, VARCHAR or OTHER. Default - * is OTHER. + * The configuration properties for mybatis core module. + * + * @since 3.0.0 */ - private JdbcType jdbcTypeForNull; + public static class CoreConfiguration { - /** - * Specifies a scroll strategy when omit it per statement settings. - */ - private ResultSetType defaultResultSetType; + /** + * Allows using RowBounds on nested statements. If allow, set the false. Default is false. + */ + private Boolean safeRowBoundsEnabled; - /** - * Configures the default executor. SIMPLE executor does nothing special. REUSE executor reuses prepared statements. - * BATCH executor reuses statements and batches updates. Default is SIMPLE. - */ - private ExecutorType defaultExecutorType; + /** + * Allows using ResultHandler on nested statements. If allow, set the false. Default is true. + */ + private Boolean safeResultHandlerEnabled; - /** - * Specifies if and how MyBatis should automatically map columns to fields/properties. NONE disables auto-mapping. - * PARTIAL will only auto-map results with no nested result mappings defined inside. FULL will auto-map result - * mappings of any complexity (containing nested or otherwise). Default is PARTIAL. - */ - private AutoMappingBehavior autoMappingBehavior; + /** + * Enables automatic mapping from classic database column names A_COLUMN to camel case classic Java property names + * aColumn. Default is true. + */ + private Boolean mapUnderscoreToCamelCase = true; - /** - * Specify the behavior when detects an unknown column (or unknown property type) of automatic mapping target. - * Default is NONE. - */ - private AutoMappingUnknownColumnBehavior autoMappingUnknownColumnBehavior; + /** + * When enabled, any method call will load all the lazy properties of the object. Otherwise, each property is loaded + * on demand (see also lazyLoadTriggerMethods). Default is false. + */ + private Boolean aggressiveLazyLoading; - /** - * Specifies the prefix string that MyBatis will add to the logger names. - */ - private String logPrefix; + /** + * Allows or disallows multiple ResultSets to be returned from a single statement (compatible driver required). + * Default is true. + */ + private Boolean multipleResultSetsEnabled; - /** - * Specifies which Object's methods trigger a lazy load. Default is [equals,clone,hashCode,toString]. - */ - private Set lazyLoadTriggerMethods; + /** + * Allows JDBC support for generated keys. A compatible driver is required. This setting forces generated keys to be + * used if set to true, as some drivers deny compatibility but still work (e.g. Derby). Default is false. + */ + private Boolean useGeneratedKeys; - /** - * Specifies which logging implementation MyBatis should use. If this setting is not present logging implementation - * will be autodiscovered. - */ - private Class logImpl; + /** + * Uses the column label instead of the column name. Different drivers behave differently in this respect. Refer to + * the driver documentation, or test out both modes to determine how your driver behaves. Default is true. + */ + private Boolean useColumnLabel; - /** - * Specifies VFS implementations. - */ - private Class vfsImpl; + /** + * Globally enables or disables any caches configured in any mapper under this configuration. Default is true. + */ + private Boolean cacheEnabled; - /** - * Specifies an sql provider class that holds provider method. This class apply to the type(or value) attribute on - * sql provider annotation(e.g. @SelectProvider), when these attribute was omitted. - */ - private Class defaultSqlProviderType; + /** + * Specifies if setters or map's put method will be called when a retrieved value is null. It is useful when you + * rely on Map.keySet() or null value initialization. Note primitives such as (int,boolean,etc.) will not be set to + * null. Default is false. + */ + private Boolean callSettersOnNulls; - /** - * Specifies the TypeHandler used by default for Enum. - */ - Class defaultEnumTypeHandler; + /** + * Allow referencing statement parameters by their actual names declared in the method signature. To use this + * feature, your project must be compiled in Java 8 with -parameters option. Default is true. + */ + private Boolean useActualParamName; - /** - * Specifies the class that provides an instance of Configuration. The returned Configuration instance is used to - * load lazy properties of deserialized objects. This class must have a method with a signature static Configuration - * getConfiguration(). - */ - private Class configurationFactory; + /** + * MyBatis, by default, returns null when all the columns of a returned row are NULL. When this setting is enabled, + * MyBatis returns an empty instance instead. Note that it is also applied to nested results (i.e. collectioin and + * association). Default is false. + */ + private Boolean returnInstanceForEmptyRow; - /** - * Specify any configuration variables. - */ - private Properties variables; + /** + * Removes extra whitespace characters from the SQL. Note that this also affects literal strings in SQL. Default is + * false. + */ + private Boolean shrinkWhitespacesInSql; + + /** + * Specifies the default value of 'nullable' attribute on 'foreach' tag. Default is false. + */ + private Boolean nullableOnForEach; + + /** + * When applying constructor auto-mapping, argument name is used to search the column to map instead of relying on + * the column order. Default is false. + */ + private Boolean argNameBasedConstructorAutoMapping; + + /** + * Globally enables or disables lazy loading. When enabled, all relations will be lazily loaded. This value can be + * superseded for a specific relation by using the fetchType attribute on it. Default is False. + */ + private Boolean lazyLoadingEnabled; + + /** + * Sets the number of seconds the driver will wait for a response from the database. + */ + private Integer defaultStatementTimeout; + + /** + * Sets the driver a hint as to control fetching size for return results. This parameter value can be override by a + * query setting. + */ + private Integer defaultFetchSize; + + /** + * MyBatis uses local cache to prevent circular references and speed up repeated nested queries. By default + * (SESSION) all queries executed during a session are cached. If localCacheScope=STATEMENT local session will be + * used just for statement execution, no data will be shared between two different calls to the same SqlSession. + * Default is SESSION. + */ + private LocalCacheScope localCacheScope; + + /** + * Specifies the JDBC type for null values when no specific JDBC type was provided for the parameter. Some drivers + * require specifying the column JDBC type but others work with generic values like NULL, VARCHAR or OTHER. Default + * is OTHER. + */ + private JdbcType jdbcTypeForNull; + + /** + * Specifies a scroll strategy when omit it per statement settings. + */ + private ResultSetType defaultResultSetType; + + /** + * Configures the default executor. SIMPLE executor does nothing special. REUSE executor reuses prepared statements. + * BATCH executor reuses statements and batches updates. Default is SIMPLE. + */ + private ExecutorType defaultExecutorType; + + /** + * Specifies if and how MyBatis should automatically map columns to fields/properties. NONE disables auto-mapping. + * PARTIAL will only auto-map results with no nested result mappings defined inside. FULL will auto-map result + * mappings of any complexity (containing nested or otherwise). Default is PARTIAL. + */ + private AutoMappingBehavior autoMappingBehavior; + + /** + * Specify the behavior when detects an unknown column (or unknown property type) of automatic mapping target. + * Default is NONE. + */ + private AutoMappingUnknownColumnBehavior autoMappingUnknownColumnBehavior; + + /** + * Specifies the prefix string that MyBatis will add to the logger names. + */ + private String logPrefix; + + /** + * Specifies which Object's methods trigger a lazy load. Default is [equals,clone,hashCode,toString]. + */ + private Set lazyLoadTriggerMethods; + + /** + * Specifies which logging implementation MyBatis should use. If this setting is not present logging implementation + * will be autodiscovered. + */ + private Class logImpl; + + /** + * Specifies VFS implementations. + */ + private Class vfsImpl; + + /** + * Specifies an sql provider class that holds provider method. This class apply to the type(or value) attribute on + * sql provider annotation(e.g. @SelectProvider), when these attribute was omitted. + */ + private Class defaultSqlProviderType; + + /** + * Specifies the TypeHandler used by default for Enum. + */ + Class defaultEnumTypeHandler; + + /** + * Specifies the class that provides an instance of Configuration. The returned Configuration instance is used to + * load lazy properties of deserialized objects. This class must have a method with a signature static Configuration + * getConfiguration(). + */ + private Class configurationFactory; + + /** + * Specify any configuration variables. + */ + private Properties variables; + + public Boolean getSafeRowBoundsEnabled() { + return safeRowBoundsEnabled; + } + + public void setSafeRowBoundsEnabled(Boolean safeRowBoundsEnabled) { + this.safeRowBoundsEnabled = safeRowBoundsEnabled; + } + + public Boolean getSafeResultHandlerEnabled() { + return safeResultHandlerEnabled; + } + + public void setSafeResultHandlerEnabled(Boolean safeResultHandlerEnabled) { + this.safeResultHandlerEnabled = safeResultHandlerEnabled; + } + + public Boolean getMapUnderscoreToCamelCase() { + return mapUnderscoreToCamelCase; + } + + public void setMapUnderscoreToCamelCase(Boolean mapUnderscoreToCamelCase) { + this.mapUnderscoreToCamelCase = mapUnderscoreToCamelCase; + } + + public Boolean getAggressiveLazyLoading() { + return aggressiveLazyLoading; + } + + public void setAggressiveLazyLoading(Boolean aggressiveLazyLoading) { + this.aggressiveLazyLoading = aggressiveLazyLoading; + } + + public Boolean getMultipleResultSetsEnabled() { + return multipleResultSetsEnabled; + } + + public void setMultipleResultSetsEnabled(Boolean multipleResultSetsEnabled) { + this.multipleResultSetsEnabled = multipleResultSetsEnabled; + } + + public Boolean getUseGeneratedKeys() { + return useGeneratedKeys; + } + + public void setUseGeneratedKeys(Boolean useGeneratedKeys) { + this.useGeneratedKeys = useGeneratedKeys; + } + + public Boolean getUseColumnLabel() { + return useColumnLabel; + } + + public void setUseColumnLabel(Boolean useColumnLabel) { + this.useColumnLabel = useColumnLabel; + } + + public Boolean getCacheEnabled() { + return cacheEnabled; + } + + public void setCacheEnabled(Boolean cacheEnabled) { + this.cacheEnabled = cacheEnabled; + } + + public Boolean getCallSettersOnNulls() { + return callSettersOnNulls; + } + + public void setCallSettersOnNulls(Boolean callSettersOnNulls) { + this.callSettersOnNulls = callSettersOnNulls; + } + + public Boolean getUseActualParamName() { + return useActualParamName; + } + + public void setUseActualParamName(Boolean useActualParamName) { + this.useActualParamName = useActualParamName; + } + + public Boolean getReturnInstanceForEmptyRow() { + return returnInstanceForEmptyRow; + } + + public void setReturnInstanceForEmptyRow(Boolean returnInstanceForEmptyRow) { + this.returnInstanceForEmptyRow = returnInstanceForEmptyRow; + } + + public Boolean getShrinkWhitespacesInSql() { + return shrinkWhitespacesInSql; + } + + public void setShrinkWhitespacesInSql(Boolean shrinkWhitespacesInSql) { + this.shrinkWhitespacesInSql = shrinkWhitespacesInSql; + } + + public Boolean getNullableOnForEach() { + return nullableOnForEach; + } + + public void setNullableOnForEach(Boolean nullableOnForEach) { + this.nullableOnForEach = nullableOnForEach; + } + + public Boolean getArgNameBasedConstructorAutoMapping() { + return argNameBasedConstructorAutoMapping; + } + + public void setArgNameBasedConstructorAutoMapping(Boolean argNameBasedConstructorAutoMapping) { + this.argNameBasedConstructorAutoMapping = argNameBasedConstructorAutoMapping; + } + + public String getLogPrefix() { + return logPrefix; + } + + public void setLogPrefix(String logPrefix) { + this.logPrefix = logPrefix; + } + + public Class getLogImpl() { + return logImpl; + } + + public void setLogImpl(Class logImpl) { + this.logImpl = logImpl; + } + + public Class getVfsImpl() { + return vfsImpl; + } + + public void setVfsImpl(Class vfsImpl) { + this.vfsImpl = vfsImpl; + } + + public Class getDefaultSqlProviderType() { + return defaultSqlProviderType; + } + + public void setDefaultSqlProviderType(Class defaultSqlProviderType) { + this.defaultSqlProviderType = defaultSqlProviderType; + } + + public LocalCacheScope getLocalCacheScope() { + return localCacheScope; + } + + public void setLocalCacheScope(LocalCacheScope localCacheScope) { + this.localCacheScope = localCacheScope; + } + + public JdbcType getJdbcTypeForNull() { + return jdbcTypeForNull; + } + + public void setJdbcTypeForNull(JdbcType jdbcTypeForNull) { + this.jdbcTypeForNull = jdbcTypeForNull; + } + + public Set getLazyLoadTriggerMethods() { + return lazyLoadTriggerMethods; + } + + public void setLazyLoadTriggerMethods(Set lazyLoadTriggerMethods) { + this.lazyLoadTriggerMethods = lazyLoadTriggerMethods; + } + + public Integer getDefaultStatementTimeout() { + return defaultStatementTimeout; + } + + public void setDefaultStatementTimeout(Integer defaultStatementTimeout) { + this.defaultStatementTimeout = defaultStatementTimeout; + } + + public Integer getDefaultFetchSize() { + return defaultFetchSize; + } + + public void setDefaultFetchSize(Integer defaultFetchSize) { + this.defaultFetchSize = defaultFetchSize; + } + + public ResultSetType getDefaultResultSetType() { + return defaultResultSetType; + } + + public void setDefaultResultSetType(ResultSetType defaultResultSetType) { + this.defaultResultSetType = defaultResultSetType; + } + + public ExecutorType getDefaultExecutorType() { + return defaultExecutorType; + } + + public void setDefaultExecutorType(ExecutorType defaultExecutorType) { + this.defaultExecutorType = defaultExecutorType; + } + + public AutoMappingBehavior getAutoMappingBehavior() { + return autoMappingBehavior; + } + + public void setAutoMappingBehavior(AutoMappingBehavior autoMappingBehavior) { + this.autoMappingBehavior = autoMappingBehavior; + } + + public AutoMappingUnknownColumnBehavior getAutoMappingUnknownColumnBehavior() { + return autoMappingUnknownColumnBehavior; + } + + public void setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior autoMappingUnknownColumnBehavior) { + this.autoMappingUnknownColumnBehavior = autoMappingUnknownColumnBehavior; + } + + public Properties getVariables() { + return variables; + } + + public void setVariables(Properties variables) { + this.variables = variables; + } + + public Boolean getLazyLoadingEnabled() { + return lazyLoadingEnabled; + } + + public void setLazyLoadingEnabled(Boolean lazyLoadingEnabled) { + this.lazyLoadingEnabled = lazyLoadingEnabled; + } + + public Class getConfigurationFactory() { + return configurationFactory; + } + + public void setConfigurationFactory(Class configurationFactory) { + this.configurationFactory = configurationFactory; + } + + public Class getDefaultEnumTypeHandler() { + return defaultEnumTypeHandler; + } + + public void setDefaultEnumTypeHandler(Class defaultEnumTypeHandler) { + this.defaultEnumTypeHandler = defaultEnumTypeHandler; + } + + void applyTo(Configuration target) { + PropertyMapper mapper = PropertyMapper.get().alwaysApplyingWhenNonNull(); + mapper.from(getSafeRowBoundsEnabled()).to(target::setSafeRowBoundsEnabled); + mapper.from(getSafeResultHandlerEnabled()).to(target::setSafeResultHandlerEnabled); + mapper.from(getMapUnderscoreToCamelCase()).to(target::setMapUnderscoreToCamelCase); + mapper.from(getAggressiveLazyLoading()).to(target::setAggressiveLazyLoading); + mapper.from(getMultipleResultSetsEnabled()).to(target::setMultipleResultSetsEnabled); + mapper.from(getUseGeneratedKeys()).to(target::setUseGeneratedKeys); + mapper.from(getUseColumnLabel()).to(target::setUseColumnLabel); + mapper.from(getCacheEnabled()).to(target::setCacheEnabled); + mapper.from(getCallSettersOnNulls()).to(target::setCallSettersOnNulls); + mapper.from(getUseActualParamName()).to(target::setUseActualParamName); + mapper.from(getReturnInstanceForEmptyRow()).to(target::setReturnInstanceForEmptyRow); + mapper.from(getShrinkWhitespacesInSql()).to(target::setShrinkWhitespacesInSql); + mapper.from(getNullableOnForEach()).to(target::setNullableOnForEach); + mapper.from(getArgNameBasedConstructorAutoMapping()).to(target::setArgNameBasedConstructorAutoMapping); + mapper.from(getLazyLoadingEnabled()).to(target::setLazyLoadingEnabled); + mapper.from(getLogPrefix()).to(target::setLogPrefix); + mapper.from(getLazyLoadTriggerMethods()).to(target::setLazyLoadTriggerMethods); + mapper.from(getDefaultStatementTimeout()).to(target::setDefaultStatementTimeout); + mapper.from(getDefaultFetchSize()).to(target::setDefaultFetchSize); + mapper.from(getLocalCacheScope()).to(target::setLocalCacheScope); + mapper.from(getJdbcTypeForNull()).to(target::setJdbcTypeForNull); + mapper.from(getDefaultResultSetType()).to(target::setDefaultResultSetType); + mapper.from(getDefaultExecutorType()).to(target::setDefaultExecutorType); + mapper.from(getAutoMappingBehavior()).to(target::setAutoMappingBehavior); + mapper.from(getAutoMappingUnknownColumnBehavior()).to(target::setAutoMappingUnknownColumnBehavior); + mapper.from(getVariables()).to(target::setVariables); + mapper.from(getLogImpl()).to(target::setLogImpl); + mapper.from(getVfsImpl()).to(target::setVfsImpl); + mapper.from(getDefaultSqlProviderType()).to(target::setDefaultSqlProviderType); + mapper.from(getConfigurationFactory()).to(target::setConfigurationFactory); + mapper.from(getDefaultEnumTypeHandler()).to(target::setDefaultEnumTypeHandler); + } - public Boolean getSafeRowBoundsEnabled() { - return safeRowBoundsEnabled; } - public void setSafeRowBoundsEnabled(Boolean safeRowBoundsEnabled) { - this.safeRowBoundsEnabled = safeRowBoundsEnabled; - } - - public Boolean getSafeResultHandlerEnabled() { - return safeResultHandlerEnabled; - } - - public void setSafeResultHandlerEnabled(Boolean safeResultHandlerEnabled) { - this.safeResultHandlerEnabled = safeResultHandlerEnabled; - } - - public Boolean getMapUnderscoreToCamelCase() { - return mapUnderscoreToCamelCase; - } - - public void setMapUnderscoreToCamelCase(Boolean mapUnderscoreToCamelCase) { - this.mapUnderscoreToCamelCase = mapUnderscoreToCamelCase; - } - - public Boolean getAggressiveLazyLoading() { - return aggressiveLazyLoading; - } - - public void setAggressiveLazyLoading(Boolean aggressiveLazyLoading) { - this.aggressiveLazyLoading = aggressiveLazyLoading; - } - - public Boolean getMultipleResultSetsEnabled() { - return multipleResultSetsEnabled; - } - - public void setMultipleResultSetsEnabled(Boolean multipleResultSetsEnabled) { - this.multipleResultSetsEnabled = multipleResultSetsEnabled; - } - - public Boolean getUseGeneratedKeys() { - return useGeneratedKeys; - } - - public void setUseGeneratedKeys(Boolean useGeneratedKeys) { - this.useGeneratedKeys = useGeneratedKeys; - } - - public Boolean getUseColumnLabel() { - return useColumnLabel; - } - - public void setUseColumnLabel(Boolean useColumnLabel) { - this.useColumnLabel = useColumnLabel; - } - - public Boolean getCacheEnabled() { - return cacheEnabled; - } - - public void setCacheEnabled(Boolean cacheEnabled) { - this.cacheEnabled = cacheEnabled; - } - - public Boolean getCallSettersOnNulls() { - return callSettersOnNulls; - } - - public void setCallSettersOnNulls(Boolean callSettersOnNulls) { - this.callSettersOnNulls = callSettersOnNulls; - } - - public Boolean getUseActualParamName() { - return useActualParamName; - } - - public void setUseActualParamName(Boolean useActualParamName) { - this.useActualParamName = useActualParamName; - } - - public Boolean getReturnInstanceForEmptyRow() { - return returnInstanceForEmptyRow; - } - - public void setReturnInstanceForEmptyRow(Boolean returnInstanceForEmptyRow) { - this.returnInstanceForEmptyRow = returnInstanceForEmptyRow; - } - - public Boolean getShrinkWhitespacesInSql() { - return shrinkWhitespacesInSql; - } - - public void setShrinkWhitespacesInSql(Boolean shrinkWhitespacesInSql) { - this.shrinkWhitespacesInSql = shrinkWhitespacesInSql; - } - - public Boolean getNullableOnForEach() { - return nullableOnForEach; - } - - public void setNullableOnForEach(Boolean nullableOnForEach) { - this.nullableOnForEach = nullableOnForEach; - } - - public Boolean getArgNameBasedConstructorAutoMapping() { - return argNameBasedConstructorAutoMapping; - } - - public void setArgNameBasedConstructorAutoMapping(Boolean argNameBasedConstructorAutoMapping) { - this.argNameBasedConstructorAutoMapping = argNameBasedConstructorAutoMapping; - } - - public String getLogPrefix() { - return logPrefix; - } - - public void setLogPrefix(String logPrefix) { - this.logPrefix = logPrefix; - } - - public Class getLogImpl() { - return logImpl; - } - - public void setLogImpl(Class logImpl) { - this.logImpl = logImpl; - } - - public Class getVfsImpl() { - return vfsImpl; - } - - public void setVfsImpl(Class vfsImpl) { - this.vfsImpl = vfsImpl; - } - - public Class getDefaultSqlProviderType() { - return defaultSqlProviderType; - } - - public void setDefaultSqlProviderType(Class defaultSqlProviderType) { - this.defaultSqlProviderType = defaultSqlProviderType; - } - - public LocalCacheScope getLocalCacheScope() { - return localCacheScope; - } - - public void setLocalCacheScope(LocalCacheScope localCacheScope) { - this.localCacheScope = localCacheScope; - } - - public JdbcType getJdbcTypeForNull() { - return jdbcTypeForNull; - } - - public void setJdbcTypeForNull(JdbcType jdbcTypeForNull) { - this.jdbcTypeForNull = jdbcTypeForNull; - } - - public Set getLazyLoadTriggerMethods() { - return lazyLoadTriggerMethods; - } - - public void setLazyLoadTriggerMethods(Set lazyLoadTriggerMethods) { - this.lazyLoadTriggerMethods = lazyLoadTriggerMethods; - } - - public Integer getDefaultStatementTimeout() { - return defaultStatementTimeout; - } - - public void setDefaultStatementTimeout(Integer defaultStatementTimeout) { - this.defaultStatementTimeout = defaultStatementTimeout; - } - - public Integer getDefaultFetchSize() { - return defaultFetchSize; - } - - public void setDefaultFetchSize(Integer defaultFetchSize) { - this.defaultFetchSize = defaultFetchSize; - } - - public ResultSetType getDefaultResultSetType() { - return defaultResultSetType; - } - - public void setDefaultResultSetType(ResultSetType defaultResultSetType) { - this.defaultResultSetType = defaultResultSetType; - } - - public ExecutorType getDefaultExecutorType() { - return defaultExecutorType; - } - - public void setDefaultExecutorType(ExecutorType defaultExecutorType) { - this.defaultExecutorType = defaultExecutorType; - } - - public AutoMappingBehavior getAutoMappingBehavior() { - return autoMappingBehavior; - } - - public void setAutoMappingBehavior(AutoMappingBehavior autoMappingBehavior) { - this.autoMappingBehavior = autoMappingBehavior; - } - - public AutoMappingUnknownColumnBehavior getAutoMappingUnknownColumnBehavior() { - return autoMappingUnknownColumnBehavior; - } - - public void setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior autoMappingUnknownColumnBehavior) { - this.autoMappingUnknownColumnBehavior = autoMappingUnknownColumnBehavior; - } - - public Properties getVariables() { - return variables; - } - - public void setVariables(Properties variables) { - this.variables = variables; - } - - public Boolean getLazyLoadingEnabled() { - return lazyLoadingEnabled; - } - - public void setLazyLoadingEnabled(Boolean lazyLoadingEnabled) { - this.lazyLoadingEnabled = lazyLoadingEnabled; - } - - public Class getConfigurationFactory() { - return configurationFactory; - } - - public void setConfigurationFactory(Class configurationFactory) { - this.configurationFactory = configurationFactory; - } - - public Class getDefaultEnumTypeHandler() { - return defaultEnumTypeHandler; - } - - public void setDefaultEnumTypeHandler(Class defaultEnumTypeHandler) { - this.defaultEnumTypeHandler = defaultEnumTypeHandler; - } - - void applyTo(Configuration target) { - PropertyMapper mapper = PropertyMapper.get().alwaysApplyingWhenNonNull(); - mapper.from(getSafeRowBoundsEnabled()).to(target::setSafeRowBoundsEnabled); - mapper.from(getSafeResultHandlerEnabled()).to(target::setSafeResultHandlerEnabled); - mapper.from(getMapUnderscoreToCamelCase()).to(target::setMapUnderscoreToCamelCase); - mapper.from(getAggressiveLazyLoading()).to(target::setAggressiveLazyLoading); - mapper.from(getMultipleResultSetsEnabled()).to(target::setMultipleResultSetsEnabled); - mapper.from(getUseGeneratedKeys()).to(target::setUseGeneratedKeys); - mapper.from(getUseColumnLabel()).to(target::setUseColumnLabel); - mapper.from(getCacheEnabled()).to(target::setCacheEnabled); - mapper.from(getCallSettersOnNulls()).to(target::setCallSettersOnNulls); - mapper.from(getUseActualParamName()).to(target::setUseActualParamName); - mapper.from(getReturnInstanceForEmptyRow()).to(target::setReturnInstanceForEmptyRow); - mapper.from(getShrinkWhitespacesInSql()).to(target::setShrinkWhitespacesInSql); - mapper.from(getNullableOnForEach()).to(target::setNullableOnForEach); - mapper.from(getArgNameBasedConstructorAutoMapping()).to(target::setArgNameBasedConstructorAutoMapping); - mapper.from(getLazyLoadingEnabled()).to(target::setLazyLoadingEnabled); - mapper.from(getLogPrefix()).to(target::setLogPrefix); - mapper.from(getLazyLoadTriggerMethods()).to(target::setLazyLoadTriggerMethods); - mapper.from(getDefaultStatementTimeout()).to(target::setDefaultStatementTimeout); - mapper.from(getDefaultFetchSize()).to(target::setDefaultFetchSize); - mapper.from(getLocalCacheScope()).to(target::setLocalCacheScope); - mapper.from(getJdbcTypeForNull()).to(target::setJdbcTypeForNull); - mapper.from(getDefaultResultSetType()).to(target::setDefaultResultSetType); - mapper.from(getDefaultExecutorType()).to(target::setDefaultExecutorType); - mapper.from(getAutoMappingBehavior()).to(target::setAutoMappingBehavior); - mapper.from(getAutoMappingUnknownColumnBehavior()).to(target::setAutoMappingUnknownColumnBehavior); - mapper.from(getVariables()).to(target::setVariables); - mapper.from(getLogImpl()).to(target::setLogImpl); - mapper.from(getVfsImpl()).to(target::setVfsImpl); - mapper.from(getDefaultSqlProviderType()).to(target::setDefaultSqlProviderType); - mapper.from(getConfigurationFactory()).to(target::setConfigurationFactory); - mapper.from(getDefaultEnumTypeHandler()).to(target::setDefaultEnumTypeHandler); - } - - } - } diff --git a/mybatis-flex-spring-boot-starter/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/mybatis-flex-spring-boot-starter/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 52ee23f0..d47a01cb 100644 --- a/mybatis-flex-spring-boot-starter/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/mybatis-flex-spring-boot-starter/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -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" } ] } diff --git a/mybatis-flex-spring-boot-starter/src/main/resources/META-INF/spring.factories b/mybatis-flex-spring-boot-starter/src/main/resources/META-INF/spring.factories index 397ff41a..b64771df 100644 --- a/mybatis-flex-spring-boot-starter/src/main/resources/META-INF/spring.factories +++ b/mybatis-flex-spring-boot-starter/src/main/resources/META-INF/spring.factories @@ -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 \ No newline at end of file diff --git a/mybatis-flex-spring-boot-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/mybatis-flex-spring-boot-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index e2dfb7d7..5d48c6ab 100644 --- a/mybatis-flex-spring-boot-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/mybatis-flex-spring-boot-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -1,2 +1,4 @@ +com.mybatisflex.spring.boot.DbAutoConfiguration +com.mybatisflex.spring.boot.MultiDataSourceAutoConfiguration com.mybatisflex.spring.boot.MybatisFlexAutoConfiguration com.mybatisflex.spring.boot.MybatisLanguageDriverAutoConfiguration diff --git a/mybatis-flex-spring/src/main/java/com/mybatisflex/spring/FlexSqlSessionFactoryBean.java b/mybatis-flex-spring/src/main/java/com/mybatisflex/spring/FlexSqlSessionFactoryBean.java index cbddee20..cc183bcc 100644 --- a/mybatis-flex-spring/src/main/java/com/mybatisflex/spring/FlexSqlSessionFactoryBean.java +++ b/mybatis-flex-spring/src/main/java/com/mybatisflex/spring/FlexSqlSessionFactoryBean.java @@ -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) { diff --git a/mybatis-flex-test/mybatis-flex-native-test/src/main/java/com/mybatisflex/test/EntityTestStarter.java b/mybatis-flex-test/mybatis-flex-native-test/src/main/java/com/mybatisflex/test/EntityTestStarter.java index 733cf98c..8abfe04a 100644 --- a/mybatis-flex-test/mybatis-flex-native-test/src/main/java/com/mybatisflex/test/EntityTestStarter.java +++ b/mybatis-flex-test/mybatis-flex-native-test/src/main/java/com/mybatisflex/test/EntityTestStarter.java @@ -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) diff --git a/mybatis-flex-test/mybatis-flex-native-test/src/main/java/com/mybatisflex/test/RowTestStarter.java b/mybatis-flex-test/mybatis-flex-native-test/src/main/java/com/mybatisflex/test/RowTestStarter.java index 5a95a270..b0cae942 100644 --- a/mybatis-flex-test/mybatis-flex-native-test/src/main/java/com/mybatisflex/test/RowTestStarter.java +++ b/mybatis-flex-test/mybatis-flex-native-test/src/main/java/com/mybatisflex/test/RowTestStarter.java @@ -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(); diff --git a/pom.xml b/pom.xml index 1ce0b741..73500a72 100644 --- a/pom.xml +++ b/pom.xml @@ -69,6 +69,7 @@ + org.mybatis mybatis