diff --git a/mybatis-flex-solon-plugin/src/main/java/com/mybatisflex/solon/MybatisFlexProperties.java b/mybatis-flex-solon-plugin/src/main/java/com/mybatisflex/solon/MybatisFlexProperties.java
new file mode 100644
index 00000000..661e7efd
--- /dev/null
+++ b/mybatis-flex-solon-plugin/src/main/java/com/mybatisflex/solon/MybatisFlexProperties.java
@@ -0,0 +1,986 @@
+/*
+ * Copyright (c) 2022-2024, 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.solon;
+
+import com.mybatisflex.core.FlexConsts;
+import com.mybatisflex.core.FlexGlobalConfig;
+import org.apache.ibatis.io.VFS;
+import org.apache.ibatis.logging.Log;
+import org.apache.ibatis.mapping.ResultSetType;
+import org.apache.ibatis.scripting.LanguageDriver;
+import org.apache.ibatis.session.*;
+import org.apache.ibatis.type.JdbcType;
+import org.apache.ibatis.type.TypeHandler;
+import org.noear.solon.core.util.ResourceUtil;
+
+import java.net.URI;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Properties;
+import java.util.Set;
+import java.util.stream.Stream;
+
+/**
+ * Mybatis-Flex 的配置属性。
+ * 参考:https://github.com/mybatis/spring-boot-starter/blob/master/mybatis-spring-boot-autoconfigure/src/main/java/org/mybatis/spring/boot/autoconfigure/MybatisProperties.java
+ *
+ * @author Eddú Meléndez
+ * @author Kazuki Shimizu
+ * @author micahel
+ * @author 王帅
+ */
+public class MybatisFlexProperties {
+ private String defaultDatasourceKey;
+
+ /**
+ *
多数据源的配置。
+ *
+ *
+ * mybatis-flex.datasource.ds1.url=***
+ * mybatis-flex.datasource.ds2.url=***
+ */
+ private Map> datasource;
+
+ /**
+ * 全局配置。
+ */
+ private GlobalConfig globalConfig;
+
+ /**
+ * MyBatis-Flex-Admin 配置。
+ */
+ private AdminConfig adminConfig;
+
+ /**
+ * Location of MyBatis xml config file.
+ */
+ private String configLocation;
+
+ /**
+ * Locations of MyBatis mapper files.
+ */
+ private String[] mapperLocations = new String[]{"classpath*:/mapper/**/*.xml"};
+
+ /**
+ * 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.solon.SqlSessionTemplate}.
+ */
+ private ExecutorType executorType;
+
+ /**
+ * The default scripting language driver class. (Available when use together with mybatis-spring 2.0.2+)
+ */
+ private Class extends LanguageDriver> 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;
+
+ /**
+ * A Configuration object for seata
+ */
+ private SeataConfig seataConfig;
+
+ public SeataConfig getSeataConfig() {
+ return seataConfig;
+ }
+
+ public void setSeataConfig(SeataConfig seataConfig) {
+ this.seataConfig = seataConfig;
+ }
+
+ public Map> getDatasource() {
+ return datasource;
+ }
+
+ public void setDatasource(Map> datasource) {
+ this.datasource = datasource;
+ }
+
+ public GlobalConfig getGlobalConfig() {
+ return globalConfig;
+ }
+
+ public void setGlobalConfig(GlobalConfig globalConfig) {
+ this.globalConfig = globalConfig;
+ }
+
+ public AdminConfig getAdminConfig() {
+ return adminConfig;
+ }
+
+ public void setAdminConfig(AdminConfig adminConfig) {
+ this.adminConfig = adminConfig;
+ }
+
+ public String getDefaultDatasourceKey() {
+ return defaultDatasourceKey;
+ }
+
+ public void setDefaultDatasourceKey(String defaultDatasourceKey) {
+ this.defaultDatasourceKey = defaultDatasourceKey;
+ }
+
+ /**
+ * @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 extends LanguageDriver> getDefaultScriptingLanguageDriver() {
+ return defaultScriptingLanguageDriver;
+ }
+
+ /**
+ * @since 2.1.0
+ */
+ public void setDefaultScriptingLanguageDriver(Class extends LanguageDriver> 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 URI[] resolveMapperLocations() {
+ return Stream.of(Optional.ofNullable(this.mapperLocations).orElse(new String[0]))
+ .flatMap(location -> Stream.of(getResources(location))).toArray(URI[]::new);
+ }
+
+ private URI[] getResources(String location) {
+ return ResourceUtil.scanResources(location).stream().map(m -> URI.create(m)).toArray(URI[]::new);
+ }
+
+ /**
+ * The configuration properties for mybatis core module.
+ *
+ * @since 3.0.0
+ */
+ public static class CoreConfiguration {
+
+ /**
+ * Allows using RowBounds on nested statements. If allow, set the false. Default is false.
+ */
+ private Boolean safeRowBoundsEnabled;
+
+ /**
+ * Allows using ResultHandler on nested statements. If allow, set the false. Default is true.
+ */
+ private Boolean safeResultHandlerEnabled;
+
+ /**
+ * 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;
+
+ /**
+ * 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;
+
+ /**
+ * Allows or disallows multiple ResultSets to be returned from a single statement (compatible driver required).
+ * Default is true.
+ */
+ private Boolean multipleResultSetsEnabled;
+
+ /**
+ * 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;
+
+ /**
+ * 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;
+
+ /**
+ * Globally enables or disables any caches configured in any mapper under this configuration. Default is true.
+ */
+ private Boolean cacheEnabled;
+
+ /**
+ * 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;
+
+ /**
+ * 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;
+
+ /**
+ * 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;
+
+ /**
+ * 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 extends Log> logImpl;
+
+ /**
+ * Specifies VFS implementations.
+ */
+ private Class extends VFS> 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 extends TypeHandler> 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 extends Log> getLogImpl() {
+ return logImpl;
+ }
+
+ public void setLogImpl(Class extends Log> logImpl) {
+ this.logImpl = logImpl;
+ }
+
+ public Class extends VFS> getVfsImpl() {
+ return vfsImpl;
+ }
+
+ public void setVfsImpl(Class extends VFS> 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 extends TypeHandler> getDefaultEnumTypeHandler() {
+ return defaultEnumTypeHandler;
+ }
+
+ public void setDefaultEnumTypeHandler(Class extends TypeHandler> defaultEnumTypeHandler) {
+ this.defaultEnumTypeHandler = defaultEnumTypeHandler;
+ }
+
+ public void applyTo(Configuration target) {
+ Optional.ofNullable(getSafeRowBoundsEnabled()).ifPresent(target::setSafeRowBoundsEnabled);
+ Optional.ofNullable(getSafeRowBoundsEnabled()).ifPresent(target::setSafeRowBoundsEnabled);
+ Optional.ofNullable(getSafeResultHandlerEnabled()).ifPresent(target::setSafeResultHandlerEnabled);
+ Optional.ofNullable(getMapUnderscoreToCamelCase()).ifPresent(target::setMapUnderscoreToCamelCase);
+ Optional.ofNullable(getAggressiveLazyLoading()).ifPresent(target::setAggressiveLazyLoading);
+ Optional.ofNullable(getMultipleResultSetsEnabled()).ifPresent(target::setMultipleResultSetsEnabled);
+ Optional.ofNullable(getUseGeneratedKeys()).ifPresent(target::setUseGeneratedKeys);
+ Optional.ofNullable(getUseColumnLabel()).ifPresent(target::setUseColumnLabel);
+ Optional.ofNullable(getCacheEnabled()).ifPresent(target::setCacheEnabled);
+ Optional.ofNullable(getCallSettersOnNulls()).ifPresent(target::setCallSettersOnNulls);
+ Optional.ofNullable(getUseActualParamName()).ifPresent(target::setUseActualParamName);
+ Optional.ofNullable(getReturnInstanceForEmptyRow()).ifPresent(target::setReturnInstanceForEmptyRow);
+ Optional.ofNullable(getShrinkWhitespacesInSql()).ifPresent(target::setShrinkWhitespacesInSql);
+ Optional.ofNullable(getNullableOnForEach()).ifPresent(target::setNullableOnForEach);
+ Optional.ofNullable(getArgNameBasedConstructorAutoMapping()).ifPresent(target::setArgNameBasedConstructorAutoMapping);
+ Optional.ofNullable(getLazyLoadingEnabled()).ifPresent(target::setLazyLoadingEnabled);
+ Optional.ofNullable(getLogPrefix()).ifPresent(target::setLogPrefix);
+ Optional.ofNullable(getLazyLoadTriggerMethods()).ifPresent(target::setLazyLoadTriggerMethods);
+ Optional.ofNullable(getDefaultStatementTimeout()).ifPresent(target::setDefaultStatementTimeout);
+ Optional.ofNullable(getDefaultFetchSize()).ifPresent(target::setDefaultFetchSize);
+ Optional.ofNullable(getLocalCacheScope()).ifPresent(target::setLocalCacheScope);
+ Optional.ofNullable(getJdbcTypeForNull()).ifPresent(target::setJdbcTypeForNull);
+ Optional.ofNullable(getDefaultResultSetType()).ifPresent(target::setDefaultResultSetType);
+ Optional.ofNullable(getDefaultExecutorType()).ifPresent(target::setDefaultExecutorType);
+ Optional.ofNullable(getAutoMappingBehavior()).ifPresent(target::setAutoMappingBehavior);
+ Optional.ofNullable(getAutoMappingUnknownColumnBehavior()).ifPresent(target::setAutoMappingUnknownColumnBehavior);
+ Optional.ofNullable(getVariables()).ifPresent(target::setVariables);
+ Optional.ofNullable(getLogImpl()).ifPresent(target::setLogImpl);
+ Optional.ofNullable(getVfsImpl()).ifPresent(target::setVfsImpl);
+ Optional.ofNullable(getDefaultSqlProviderType()).ifPresent(target::setDefaultSqlProviderType);
+ Optional.ofNullable(getConfigurationFactory()).ifPresent(target::setConfigurationFactory);
+ Optional.ofNullable(getDefaultEnumTypeHandler()).ifPresent(target::setDefaultEnumTypeHandler);
+ }
+
+ }
+
+ /**
+ * {@link FlexGlobalConfig} 配置。
+ *
+ * @author 王帅
+ * @since 2023-06-21
+ */
+ public static class GlobalConfig {
+
+ /**
+ * 启动是否打印 banner 和 版本号。
+ */
+ private boolean printBanner = true;
+
+
+ /**
+ * 全局的 ID 生成策略配置,当 @Id 未配置 或者 配置 KeyType 为 None 时
+ * 使用当前全局配置。
+ */
+ private FlexGlobalConfig.KeyConfig keyConfig;
+
+ /**
+ * 逻辑删除数据存在标记值。
+ */
+ private Object normalValueOfLogicDelete = FlexConsts.LOGIC_DELETE_NORMAL;
+
+ /**
+ * 逻辑删除数据删除标记值。
+ */
+ private Object deletedValueOfLogicDelete = FlexConsts.LOGIC_DELETE_DELETED;
+
+
+ /**
+ * 默认的分页查询时的每页数据量。
+ */
+ private int defaultPageSize = 10;
+
+
+ /**
+ * 默认的 Relation 注解查询深度。
+ */
+ private int defaultRelationQueryDepth = 2;
+
+ /**
+ * 默认的逻辑删除字段。
+ */
+ private String logicDeleteColumn;
+
+ /**
+ * 默认的多租户字段。
+ */
+ private String tenantColumn;
+
+ /**
+ * 默认的乐观锁字段。
+ */
+ private String versionColumn;
+
+
+ public boolean isPrintBanner() {
+ return printBanner;
+ }
+
+ public void setPrintBanner(boolean printBanner) {
+ this.printBanner = printBanner;
+ }
+
+ public FlexGlobalConfig.KeyConfig getKeyConfig() {
+ return keyConfig;
+ }
+
+ public void setKeyConfig(FlexGlobalConfig.KeyConfig keyConfig) {
+ this.keyConfig = keyConfig;
+ }
+
+ public Object getNormalValueOfLogicDelete() {
+ return normalValueOfLogicDelete;
+ }
+
+ public void setNormalValueOfLogicDelete(Object normalValueOfLogicDelete) {
+ this.normalValueOfLogicDelete = normalValueOfLogicDelete;
+ }
+
+ public Object getDeletedValueOfLogicDelete() {
+ return deletedValueOfLogicDelete;
+ }
+
+ public void setDeletedValueOfLogicDelete(Object deletedValueOfLogicDelete) {
+ this.deletedValueOfLogicDelete = deletedValueOfLogicDelete;
+ }
+
+ public int getDefaultPageSize() {
+ return defaultPageSize;
+ }
+
+ public void setDefaultPageSize(int defaultPageSize) {
+ this.defaultPageSize = defaultPageSize;
+ }
+
+ public int getDefaultRelationQueryDepth() {
+ return defaultRelationQueryDepth;
+ }
+
+ public void setDefaultRelationQueryDepth(int defaultRelationQueryDepth) {
+ this.defaultRelationQueryDepth = defaultRelationQueryDepth;
+ }
+
+ public String getLogicDeleteColumn() {
+ return logicDeleteColumn;
+ }
+
+ public void setLogicDeleteColumn(String logicDeleteColumn) {
+ this.logicDeleteColumn = logicDeleteColumn;
+ }
+
+ public String getTenantColumn() {
+ return tenantColumn;
+ }
+
+ public void setTenantColumn(String tenantColumn) {
+ this.tenantColumn = tenantColumn;
+ }
+
+ public String getVersionColumn() {
+ return versionColumn;
+ }
+
+ public void setVersionColumn(String versionColumn) {
+ this.versionColumn = versionColumn;
+ }
+
+ public void applyTo(FlexGlobalConfig target) {
+ Optional.ofNullable(isPrintBanner()).ifPresent(target::setPrintBanner);
+ Optional.ofNullable(getKeyConfig()).ifPresent(target::setKeyConfig);
+ Optional.ofNullable(getNormalValueOfLogicDelete()).ifPresent(target::setNormalValueOfLogicDelete);
+ Optional.ofNullable(getDeletedValueOfLogicDelete()).ifPresent(target::setDeletedValueOfLogicDelete);
+ Optional.ofNullable(getDefaultPageSize()).ifPresent(target::setDefaultPageSize);
+ Optional.ofNullable(getDefaultRelationQueryDepth()).ifPresent(target::setDefaultRelationQueryDepth);
+ Optional.ofNullable(getLogicDeleteColumn()).ifPresent(target::setLogicDeleteColumn);
+ Optional.ofNullable(getVersionColumn()).ifPresent(target::setVersionColumn);
+ Optional.ofNullable(getTenantColumn()).ifPresent(target::setTenantColumn);
+ }
+
+ }
+
+ /**
+ * MyBatis Flex Admin 配置。
+ *
+ * @author 王帅
+ * @since 2023-07-02
+ */
+ public static class AdminConfig {
+
+ /**
+ * 启用服务。
+ */
+ private boolean enable;
+
+ /**
+ * 连接端点。
+ */
+ private String endpoint;
+
+ /**
+ * 秘密密钥。
+ */
+ private String secretKey;
+
+ public boolean isEnable() {
+ return enable;
+ }
+
+ public void setEnable(boolean enable) {
+ this.enable = enable;
+ }
+
+ public String getEndpoint() {
+ return endpoint;
+ }
+
+ public void setEndpoint(String endpoint) {
+ this.endpoint = endpoint;
+ }
+
+ public String getSecretKey() {
+ return secretKey;
+ }
+
+ public void setSecretKey(String secretKey) {
+ this.secretKey = secretKey;
+ }
+
+ }
+
+ /**
+ * Seata 配置
+ *
+ * @author life
+ */
+ public static class SeataConfig {
+
+ /**
+ * 是否开启
+ */
+ private boolean enable = false;
+
+ /**
+ * 事务模式支持,只支持XA或者AT
+ */
+ private SeataMode seataMode = SeataMode.AT;
+
+ public boolean isEnable() {
+ return enable;
+ }
+
+ public void setEnable(boolean enable) {
+ this.enable = enable;
+ }
+
+ public SeataMode getSeataMode() {
+ return seataMode;
+ }
+
+ public void setSeataMode(SeataMode seataMode) {
+ this.seataMode = seataMode;
+ }
+
+ }
+
+ /**
+ * @author life
+ */
+ public enum SeataMode {
+ XA,
+ AT
+ }
+}
diff --git a/mybatis-flex-solon-plugin/src/main/java/com/mybatisflex/solon/integration/MybatisAdapterFlex.java b/mybatis-flex-solon-plugin/src/main/java/com/mybatisflex/solon/integration/MybatisAdapterFlex.java
index b16c9b63..a99eb3c6 100644
--- a/mybatis-flex-solon-plugin/src/main/java/com/mybatisflex/solon/integration/MybatisAdapterFlex.java
+++ b/mybatis-flex-solon-plugin/src/main/java/com/mybatisflex/solon/integration/MybatisAdapterFlex.java
@@ -20,6 +20,7 @@ import com.mybatisflex.core.FlexGlobalConfig;
import com.mybatisflex.core.mybatis.FlexConfiguration;
import com.mybatisflex.core.mybatis.FlexSqlSessionFactoryBuilder;
import com.mybatisflex.core.row.RowMapperInvoker;
+import com.mybatisflex.solon.MybatisFlexProperties;
import com.mybatisflex.solon.mybtais.MybatisAdapterDefault;
import org.apache.ibatis.mapping.Environment;
import org.apache.ibatis.session.SqlSessionFactory;
@@ -28,7 +29,6 @@ import org.noear.solon.core.BeanWrap;
import org.noear.solon.core.Props;
import org.noear.solon.core.VarHolder;
import org.noear.solon.core.event.EventBus;
-import org.noear.solon.core.util.ClassUtil;
import javax.sql.DataSource;
@@ -43,18 +43,16 @@ public class MybatisAdapterFlex extends MybatisAdapterDefault {
private FlexGlobalConfig globalConfig;
private RowMapperInvoker rowMapperInvoker;
private Class> typeAliasesBaseType;
+ private MybatisFlexProperties flexProperties;
- protected MybatisAdapterFlex(BeanWrap dsWrap) {
- super(dsWrap);
+ protected MybatisAdapterFlex(BeanWrap dsWrap, Props flexProps, MybatisFlexProperties flexProperties) {
+ this.factoryBuilderPlus = new FlexSqlSessionFactoryBuilder();
+ this.flexProperties = flexProperties;
- factoryBuilderPlus = new FlexSqlSessionFactoryBuilder();
- initAfter(dsWrap);
- }
+ //初始化开始
+ initStart(dsWrap, flexProps);
- protected MybatisAdapterFlex(BeanWrap dsWrap, Props dsProps) {
- super(dsWrap, dsProps);
-
- factoryBuilderPlus = new FlexSqlSessionFactoryBuilder();
+ //初始化之前
initAfter(dsWrap);
}
@@ -72,17 +70,14 @@ public class MybatisAdapterFlex extends MybatisAdapterDefault {
//for configuration section
config = new FlexConfiguration(environment);
- String typeAliasesBaseTypeStr = dsProps.get("typeAliasesSuperType");
- if (Utils.isNotEmpty(typeAliasesBaseTypeStr)) {
- typeAliasesBaseType = ClassUtil.loadClass(typeAliasesBaseTypeStr);
+ if (Utils.isNotEmpty(flexProperties.getConfigurationProperties())) {
+ Utils.injectProperties(config, flexProperties.getConfigurationProperties());
}
- Props cfgProps = dsProps.getProp("configuration");
- if (cfgProps.size() > 0) {
- Utils.injectProperties(config, cfgProps);
+ if (flexProperties.getConfiguration() != null) {
+ flexProperties.getConfiguration().applyTo(config);
}
-
//for globalConfig section
if (dsWrap.typed()) {
globalConfig = FlexGlobalConfig.getDefaultConfig();
@@ -90,15 +85,15 @@ public class MybatisAdapterFlex extends MybatisAdapterDefault {
globalConfig = new FlexGlobalConfig();
}
+ if (flexProperties.getGlobalConfig() != null) {
+ flexProperties.getGlobalConfig().applyTo(globalConfig);
+ }
+
if (globalConfig.getKeyConfig() == null) {
+ //如果没有,给个默认值
globalConfig.setKeyConfig(new FlexGlobalConfig.KeyConfig());
}
- Props globalProps = dsProps.getProp("globalConfig");
- if (globalProps.size() > 0) {
- //尝试配置注入
- Utils.injectProperties(globalConfig, globalProps);
- }
globalConfig.setConfiguration(config);
FlexGlobalConfig.setConfig(environment.getId(), globalConfig, false);
@@ -107,6 +102,12 @@ public class MybatisAdapterFlex extends MybatisAdapterDefault {
EventBus.publish(globalConfig);
}
+ @Override
+ protected void loadConfiguration() {
+ typeAliasesBaseType = flexProperties.getTypeAliasesSuperType();
+ super.loadConfiguration();
+ }
+
/**
* 获取全局配置
*/
diff --git a/mybatis-flex-solon-plugin/src/main/java/com/mybatisflex/solon/integration/XPluginImpl.java b/mybatis-flex-solon-plugin/src/main/java/com/mybatisflex/solon/integration/XPluginImpl.java
index 3de974a7..fd8f4aa4 100644
--- a/mybatis-flex-solon-plugin/src/main/java/com/mybatisflex/solon/integration/XPluginImpl.java
+++ b/mybatis-flex-solon-plugin/src/main/java/com/mybatisflex/solon/integration/XPluginImpl.java
@@ -20,12 +20,15 @@ import com.mybatisflex.annotation.UseDataSource;
import com.mybatisflex.core.FlexConsts;
import com.mybatisflex.core.FlexGlobalConfig;
import com.mybatisflex.core.MybatisFlexBootstrap;
+import com.mybatisflex.core.datasource.DataSourceBuilder;
import com.mybatisflex.core.datasource.DataSourceKey;
import com.mybatisflex.core.datasource.FlexDataSource;
import com.mybatisflex.core.mybatis.FlexConfiguration;
import com.mybatisflex.core.row.RowMapperInvoker;
+import com.mybatisflex.solon.MybatisFlexProperties;
import com.mybatisflex.solon.aot.MybatisRuntimeNativeRegistrar;
import org.apache.ibatis.session.SqlSessionFactory;
+import org.noear.solon.Utils;
import org.noear.solon.annotation.Inject;
import org.noear.solon.aot.RuntimeNativeRegistrar;
import org.noear.solon.core.AppContext;
@@ -35,7 +38,6 @@ import org.noear.solon.core.Props;
import org.noear.solon.core.runtime.NativeDetector;
import org.noear.solon.core.util.ClassUtil;
import org.noear.solon.core.util.TmplUtil;
-import org.noear.solon.data.datasource.DsUtils;
import javax.sql.DataSource;
import java.util.Map;
@@ -47,9 +49,7 @@ import java.util.Map;
* @since 2.2
*/
public class XPluginImpl implements Plugin {
- private static final String CONFIG_PREFIX = "mybatisFlex";
- private static final String CONFIG_DS_PREFIX = "mybatisFlex.datasource";
- private static final String CONFIG_DS_DEF = "mybatisFlex.defaultDatasourceKey";
+ private static final String CONFIG_PREFIX = "mybatisFlex.";
private static MybatisAdapterFlex adapterFlex;
@@ -57,11 +57,20 @@ public class XPluginImpl implements Plugin {
return adapterFlex;
}
+ private Props flexProps;
+ private MybatisFlexProperties flexProperties;
+
@Override
public void start(AppContext context) throws Throwable {
// 注册动态数据源的事务路由
//TranManager.routing(FlexDataSource.class, new FlexDataSourceRouting());
+ flexProps = context.cfg().getProp(CONFIG_PREFIX);
+ flexProperties = flexProps.toBean(MybatisFlexProperties.class);
+ if (flexProperties == null) {
+ flexProperties = new MybatisFlexProperties();
+ }
+
// 订阅数据源
context.subWrapsOfType(DataSource.class, bw -> {
loadDs(context, bw);
@@ -72,18 +81,12 @@ public class XPluginImpl implements Plugin {
context.wrapAndPut(MybatisRuntimeNativeRegistrar.class);
}
-
// 构建 mf 配置的数据源
- Class> dsDefClz = ClassUtil.loadClass("com.zaxxer.hikari.HikariDataSource");
- Props dsProps = context.cfg().getProp(CONFIG_DS_PREFIX);
- String dsDef = context.cfg().get(CONFIG_DS_DEF);
- if (dsProps.size() > 0) {
- Map dsMap = DsUtils.buildDsMap(dsProps, dsDefClz);
-
- for (Map.Entry entry : dsMap.entrySet()) {
+ if (Utils.isNotEmpty(flexProperties.getDatasource())) {
+ for (Map.Entry> entry : flexProperties.getDatasource().entrySet()) {
String dsName = entry.getKey();
- DataSource ds = entry.getValue();
- BeanWrap bw = context.wrap(dsName, ds, dsName.equals(dsDef));
+ DataSource ds = new DataSourceBuilder(entry.getValue()).build();
+ BeanWrap bw = context.wrap(dsName, ds, dsName.equals(flexProperties.getDefaultDatasourceKey()));
loadDs(context, bw);
}
}
@@ -106,7 +109,7 @@ public class XPluginImpl implements Plugin {
private void initDo(AppContext context) {
BeanWrap dsBw = context.wrap(FlexConsts.NAME, MybatisFlexBootstrap.getInstance().getDataSource(), true);
- MybatisAdapterFlex dsFlex = new MybatisAdapterFlex(dsBw, context.cfg().getProp(CONFIG_PREFIX));
+ MybatisAdapterFlex dsFlex = new MybatisAdapterFlex(dsBw, flexProps, flexProperties);
dsFlex.mapperPublish();
adapterFlex = dsFlex;
diff --git a/mybatis-flex-solon-plugin/src/main/java/com/mybatisflex/solon/mybtais/MybatisAdapterDefault.java b/mybatis-flex-solon-plugin/src/main/java/com/mybatisflex/solon/mybtais/MybatisAdapterDefault.java
index c5a13f6a..68f205eb 100644
--- a/mybatis-flex-solon-plugin/src/main/java/com/mybatisflex/solon/mybtais/MybatisAdapterDefault.java
+++ b/mybatis-flex-solon-plugin/src/main/java/com/mybatisflex/solon/mybtais/MybatisAdapterDefault.java
@@ -11,7 +11,6 @@ import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.apache.ibatis.transaction.TransactionFactory;
import org.apache.ibatis.type.TypeHandler;
-import org.noear.solon.Solon;
import org.noear.solon.Utils;
import org.noear.solon.core.BeanWrap;
import org.noear.solon.core.LifecycleIndex;
@@ -39,28 +38,21 @@ import java.util.Map;
public class MybatisAdapterDefault {
protected static final Logger log = LoggerFactory.getLogger(MybatisAdapterDefault.class);
- protected final BeanWrap dsWrap;
- protected final Props dsProps;
+ protected BeanWrap dsWrap;
+ protected Props dsProps;
//mapper 注解验证启用?
- protected final boolean mapperVerifyEnabled;
+ protected boolean mapperVerifyEnabled;
protected Configuration config;
protected SqlSessionFactory factory;
protected List mappers = new ArrayList<>();
protected SqlSessionFactoryBuilder factoryBuilder;
- /**
- * 构建Sql工厂适配器,使用默认的 typeAliases 和 mappers 配置
- */
- protected MybatisAdapterDefault(BeanWrap dsWrap) {
- this(dsWrap, Solon.cfg().getProp("mybatis"));
- }
-
/**
* 构建Sql工厂适配器,使用属性配置
*/
- protected MybatisAdapterDefault(BeanWrap dsWrap, Props dsProps) {
+ protected void initStart(BeanWrap dsWrap, Props dsProps) {
this.dsWrap = dsWrap;
if (dsProps == null) {
this.dsProps = new Props();
@@ -80,6 +72,7 @@ public class MybatisAdapterDefault {
TransactionFactory tf = new SolonManagedTransactionFactory();
Environment environment = new Environment(dataSourceId, tf, dataSource);
+ //1.初始化配置器
initConfiguration(environment);
//加载插件(通过Bean)
@@ -91,11 +84,11 @@ public class MybatisAdapterDefault {
});
});
- //1.分发事件,推给扩展处理
+ //2.分发事件,推给扩展处理
EventBus.publish(config);
- //2.初始化(顺序不能乱)
- initDo();
+ //3.加载配置器的内容(顺序不能乱)
+ loadConfiguration();
dsWrap.context().getBeanAsync(SqlSessionFactoryBuilder.class, bean -> {
factoryBuilder = bean;
@@ -124,19 +117,19 @@ public class MybatisAdapterDefault {
return true;
}
- protected boolean isTypeAliasesKey(String key){
+ protected boolean isTypeAliasesKey(String key) {
return key.startsWith("typeAliases[") || key.equals("typeAliases");
}
- protected boolean isTypeHandlersKey(String key){
+ protected boolean isTypeHandlersKey(String key) {
return key.startsWith("typeHandlers[") || key.equals("typeHandlers");
}
- protected boolean isMappersKey(String key){
+ protected boolean isMappersKey(String key) {
return key.startsWith("mappers[") || key.equals("mappers");
}
- protected void initDo() {
+ protected void loadConfiguration() {
//for typeAliases & typeHandlers section
dsProps.forEach((k, v) -> {
if (k instanceof String && v instanceof String) {
@@ -283,9 +276,9 @@ public class MybatisAdapterDefault {
MybatisMapperInterceptor handler = new MybatisMapperInterceptor(getFactory(), mapperClz);
mapper = Proxy.newProxyInstance(
- mapperClz.getClassLoader(),
- new Class[]{mapperClz},
- handler);
+ mapperClz.getClassLoader(),
+ new Class[]{mapperClz},
+ handler);
mapperCached.put(mapperClz, mapper);
}
}