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 b5c23e97..2502e236 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 @@ -20,6 +20,7 @@ import com.mybatisflex.core.FlexGlobalConfig; import com.mybatisflex.core.dialect.DbType; import com.mybatisflex.core.dialect.DbTypeUtil; import com.mybatisflex.core.exception.FlexExceptions; +import org.apache.ibatis.builder.xml.XMLConfigBuilder; import org.apache.ibatis.exceptions.ExceptionFactory; import org.apache.ibatis.executor.ErrorContext; import org.apache.ibatis.session.Configuration; @@ -28,14 +29,39 @@ import org.apache.ibatis.session.SqlSessionFactoryBuilder; import java.io.IOException; import java.io.InputStream; +import java.io.Reader; import java.util.Properties; public class FlexSqlSessionFactoryBuilder extends SqlSessionFactoryBuilder { + @Override + public SqlSessionFactory build(Reader reader, String environment, Properties properties) { + try { + // 需要 mybaits v3.5.13+ + // https://github.com/mybatis/mybatis-3/commit/d7826d71a7005a8b4d4e0e7a020db0f6c7e253a4 + XMLConfigBuilder parser = new XMLConfigBuilder(FlexConfiguration.class, reader, environment, properties); + return build(parser.parse()); + } catch (Exception e) { + throw ExceptionFactory.wrapException("Error building SqlSession.", e); + } finally { + ErrorContext.instance().reset(); + try { + if (reader != null) { + reader.close(); + } + } catch (IOException e) { + // Intentionally ignore. Prefer previous error. + } + } + } + + @Override public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) { try { - FlexXMLConfigBuilder parser = new FlexXMLConfigBuilder(inputStream, environment, properties); + // 需要 mybaits v3.5.13+ + // https://github.com/mybatis/mybatis-3/commit/d7826d71a7005a8b4d4e0e7a020db0f6c7e253a4 + XMLConfigBuilder parser = new XMLConfigBuilder(FlexConfiguration.class, inputStream, environment, properties); return build(parser.parse()); } catch (Exception e) { throw ExceptionFactory.wrapException("Error building SqlSession.", e); diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/mybatis/FlexXMLConfigBuilder.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/mybatis/FlexXMLConfigBuilder.java deleted file mode 100644 index b636f5b1..00000000 --- a/mybatis-flex-core/src/main/java/com/mybatisflex/core/mybatis/FlexXMLConfigBuilder.java +++ /dev/null @@ -1,409 +0,0 @@ -/** - * 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 org.apache.ibatis.builder.BaseBuilder; -import org.apache.ibatis.builder.BuilderException; -import org.apache.ibatis.builder.xml.XMLMapperBuilder; -import org.apache.ibatis.builder.xml.XMLMapperEntityResolver; -import org.apache.ibatis.datasource.DataSourceFactory; -import org.apache.ibatis.executor.ErrorContext; -import org.apache.ibatis.executor.loader.ProxyFactory; -import org.apache.ibatis.io.Resources; -import org.apache.ibatis.io.VFS; -import org.apache.ibatis.logging.Log; -import org.apache.ibatis.mapping.DatabaseIdProvider; -import org.apache.ibatis.mapping.Environment; -import org.apache.ibatis.parsing.XNode; -import org.apache.ibatis.parsing.XPathParser; -import org.apache.ibatis.plugin.Interceptor; -import org.apache.ibatis.reflection.DefaultReflectorFactory; -import org.apache.ibatis.reflection.MetaClass; -import org.apache.ibatis.reflection.ReflectorFactory; -import org.apache.ibatis.reflection.factory.ObjectFactory; -import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory; -import org.apache.ibatis.session.*; -import org.apache.ibatis.transaction.TransactionFactory; -import org.apache.ibatis.type.JdbcType; - -import javax.sql.DataSource; -import java.io.InputStream; -import java.io.Reader; -import java.util.Properties; - -/** - * @author Clinton Begin - * @author Kazuki Shimizu - */ -public class FlexXMLConfigBuilder extends BaseBuilder { - - private boolean parsed; - private final XPathParser parser; - private String environment; - private final ReflectorFactory localReflectorFactory = new DefaultReflectorFactory(); - - public FlexXMLConfigBuilder(Reader reader) { - this(reader, null, null); - } - - public FlexXMLConfigBuilder(Reader reader, String environment) { - this(reader, environment, null); - } - - public FlexXMLConfigBuilder(Reader reader, String environment, Properties props) { - this(new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props); - } - - public FlexXMLConfigBuilder(InputStream inputStream) { - this(inputStream, null, null); - } - - public FlexXMLConfigBuilder(InputStream inputStream, String environment) { - this(inputStream, environment, null); - } - - public FlexXMLConfigBuilder(InputStream inputStream, String environment, Properties props) { - this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props); - } - - private FlexXMLConfigBuilder(XPathParser parser, String environment, Properties props) { - // 目前暂时只能通过这里替换 configuration - // 新的版本已经支持自定义 configuration,但需新版本 release: - // 详情 https://github.com/mybatis/mybatis-3/commit/d7826d71a7005a8b4d4e0e7a020db0f6c7e253a4 - super(new FlexConfiguration()); - ErrorContext.instance().resource("SQL Mapper Configuration"); - this.configuration.setVariables(props); - this.parsed = false; - this.environment = environment; - this.parser = parser; - } - - public Configuration parse() { - if (parsed) { - throw new BuilderException("Each XMLConfigBuilder can only be used once."); - } - parsed = true; - parseConfiguration(parser.evalNode("/configuration")); - return configuration; - } - - private void parseConfiguration(XNode root) { - try { - // issue #117 read properties first - propertiesElement(root.evalNode("properties")); - Properties settings = settingsAsProperties(root.evalNode("settings")); - loadCustomVfs(settings); - loadCustomLogImpl(settings); - typeAliasesElement(root.evalNode("typeAliases")); - pluginElement(root.evalNode("plugins")); - objectFactoryElement(root.evalNode("objectFactory")); - objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); - reflectorFactoryElement(root.evalNode("reflectorFactory")); - settingsElement(settings); - // read it after objectFactory and objectWrapperFactory issue #631 - environmentsElement(root.evalNode("environments")); - databaseIdProviderElement(root.evalNode("databaseIdProvider")); - typeHandlerElement(root.evalNode("typeHandlers")); - mapperElement(root.evalNode("mappers")); - } catch (Exception e) { - throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e); - } - } - - private Properties settingsAsProperties(XNode context) { - if (context == null) { - return new Properties(); - } - Properties props = context.getChildrenAsProperties(); - // Check that all settings are known to the configuration class - MetaClass metaConfig = MetaClass.forClass(Configuration.class, localReflectorFactory); - for (Object key : props.keySet()) { - if (!metaConfig.hasSetter(String.valueOf(key))) { - throw new BuilderException("The setting " + key + " is not known. Make sure you spelled it correctly (case sensitive)."); - } - } - return props; - } - - private void loadCustomVfs(Properties props) throws ClassNotFoundException { - String value = props.getProperty("vfsImpl"); - if (value != null) { - String[] clazzes = value.split(","); - for (String clazz : clazzes) { - if (!clazz.isEmpty()) { - @SuppressWarnings("unchecked") - Class vfsImpl = (Class)Resources.classForName(clazz); - configuration.setVfsImpl(vfsImpl); - } - } - } - } - - private void loadCustomLogImpl(Properties props) { - Class logImpl = resolveClass(props.getProperty("logImpl")); - configuration.setLogImpl(logImpl); - } - - private void typeAliasesElement(XNode parent) { - if (parent != null) { - for (XNode child : parent.getChildren()) { - if ("package".equals(child.getName())) { - String typeAliasPackage = child.getStringAttribute("name"); - configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage); - } else { - String alias = child.getStringAttribute("alias"); - String type = child.getStringAttribute("type"); - try { - Class clazz = Resources.classForName(type); - if (alias == null) { - typeAliasRegistry.registerAlias(clazz); - } else { - typeAliasRegistry.registerAlias(alias, clazz); - } - } catch (ClassNotFoundException e) { - throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e); - } - } - } - } - } - - private void pluginElement(XNode parent) throws Exception { - if (parent != null) { - for (XNode child : parent.getChildren()) { - String interceptor = child.getStringAttribute("interceptor"); - Properties properties = child.getChildrenAsProperties(); - Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).getDeclaredConstructor().newInstance(); - interceptorInstance.setProperties(properties); - configuration.addInterceptor(interceptorInstance); - } - } - } - - private void objectFactoryElement(XNode context) throws Exception { - if (context != null) { - String type = context.getStringAttribute("type"); - Properties properties = context.getChildrenAsProperties(); - ObjectFactory factory = (ObjectFactory) resolveClass(type).getDeclaredConstructor().newInstance(); - factory.setProperties(properties); - configuration.setObjectFactory(factory); - } - } - - private void objectWrapperFactoryElement(XNode context) throws Exception { - if (context != null) { - String type = context.getStringAttribute("type"); - ObjectWrapperFactory factory = (ObjectWrapperFactory) resolveClass(type).getDeclaredConstructor().newInstance(); - configuration.setObjectWrapperFactory(factory); - } - } - - private void reflectorFactoryElement(XNode context) throws Exception { - if (context != null) { - String type = context.getStringAttribute("type"); - ReflectorFactory factory = (ReflectorFactory) resolveClass(type).getDeclaredConstructor().newInstance(); - configuration.setReflectorFactory(factory); - } - } - - private void propertiesElement(XNode context) throws Exception { - if (context != null) { - Properties defaults = context.getChildrenAsProperties(); - String resource = context.getStringAttribute("resource"); - String url = context.getStringAttribute("url"); - if (resource != null && url != null) { - throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference. Please specify one or the other."); - } - if (resource != null) { - defaults.putAll(Resources.getResourceAsProperties(resource)); - } else if (url != null) { - defaults.putAll(Resources.getUrlAsProperties(url)); - } - Properties vars = configuration.getVariables(); - if (vars != null) { - defaults.putAll(vars); - } - parser.setVariables(defaults); - configuration.setVariables(defaults); - } - } - - private void settingsElement(Properties props) { - configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL"))); - configuration.setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior.valueOf(props.getProperty("autoMappingUnknownColumnBehavior", "NONE"))); - configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true)); - configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory"))); - configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false)); - configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading"), false)); - configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true)); - configuration.setUseColumnLabel(booleanValueOf(props.getProperty("useColumnLabel"), true)); - configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys"), false)); - configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE"))); - configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null)); - configuration.setDefaultFetchSize(integerValueOf(props.getProperty("defaultFetchSize"), null)); - configuration.setDefaultResultSetType(resolveResultSetType(props.getProperty("defaultResultSetType"))); - configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false)); - configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false)); - configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION"))); - configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER"))); - configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString")); - configuration.setSafeResultHandlerEnabled(booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true)); - configuration.setDefaultScriptingLanguage(resolveClass(props.getProperty("defaultScriptingLanguage"))); - configuration.setDefaultEnumTypeHandler(resolveClass(props.getProperty("defaultEnumTypeHandler"))); - configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false)); - configuration.setUseActualParamName(booleanValueOf(props.getProperty("useActualParamName"), true)); - configuration.setReturnInstanceForEmptyRow(booleanValueOf(props.getProperty("returnInstanceForEmptyRow"), false)); - configuration.setLogPrefix(props.getProperty("logPrefix")); - configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory"))); - configuration.setShrinkWhitespacesInSql(booleanValueOf(props.getProperty("shrinkWhitespacesInSql"), false)); - configuration.setArgNameBasedConstructorAutoMapping(booleanValueOf(props.getProperty("argNameBasedConstructorAutoMapping"), false)); - configuration.setDefaultSqlProviderType(resolveClass(props.getProperty("defaultSqlProviderType"))); - configuration.setNullableOnForEach(booleanValueOf(props.getProperty("nullableOnForEach"), false)); - } - - private void environmentsElement(XNode context) throws Exception { - if (context != null) { - if (environment == null) { - environment = context.getStringAttribute("default"); - } - for (XNode child : context.getChildren()) { - String id = child.getStringAttribute("id"); - if (isSpecifiedEnvironment(id)) { - TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager")); - DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource")); - DataSource dataSource = dsFactory.getDataSource(); - Environment.Builder environmentBuilder = new Environment.Builder(id) - .transactionFactory(txFactory) - .dataSource(dataSource); - configuration.setEnvironment(environmentBuilder.build()); - break; - } - } - } - } - - private void databaseIdProviderElement(XNode context) throws Exception { - DatabaseIdProvider databaseIdProvider = null; - if (context != null) { - String type = context.getStringAttribute("type"); - // awful patch to keep backward compatibility - if ("VENDOR".equals(type)) { - type = "DB_VENDOR"; - } - Properties properties = context.getChildrenAsProperties(); - databaseIdProvider = (DatabaseIdProvider) resolveClass(type).getDeclaredConstructor().newInstance(); - databaseIdProvider.setProperties(properties); - } - Environment environment = configuration.getEnvironment(); - if (environment != null && databaseIdProvider != null) { - String databaseId = databaseIdProvider.getDatabaseId(environment.getDataSource()); - configuration.setDatabaseId(databaseId); - } - } - - private TransactionFactory transactionManagerElement(XNode context) throws Exception { - if (context != null) { - String type = context.getStringAttribute("type"); - Properties props = context.getChildrenAsProperties(); - TransactionFactory factory = (TransactionFactory) resolveClass(type).getDeclaredConstructor().newInstance(); - factory.setProperties(props); - return factory; - } - throw new BuilderException("Environment declaration requires a TransactionFactory."); - } - - private DataSourceFactory dataSourceElement(XNode context) throws Exception { - if (context != null) { - String type = context.getStringAttribute("type"); - Properties props = context.getChildrenAsProperties(); - DataSourceFactory factory = (DataSourceFactory) resolveClass(type).getDeclaredConstructor().newInstance(); - factory.setProperties(props); - return factory; - } - throw new BuilderException("Environment declaration requires a DataSourceFactory."); - } - - private void typeHandlerElement(XNode parent) { - if (parent != null) { - for (XNode child : parent.getChildren()) { - if ("package".equals(child.getName())) { - String typeHandlerPackage = child.getStringAttribute("name"); - typeHandlerRegistry.register(typeHandlerPackage); - } else { - String javaTypeName = child.getStringAttribute("javaType"); - String jdbcTypeName = child.getStringAttribute("jdbcType"); - String handlerTypeName = child.getStringAttribute("handler"); - Class javaTypeClass = resolveClass(javaTypeName); - JdbcType jdbcType = resolveJdbcType(jdbcTypeName); - Class typeHandlerClass = resolveClass(handlerTypeName); - if (javaTypeClass != null) { - if (jdbcType == null) { - typeHandlerRegistry.register(javaTypeClass, typeHandlerClass); - } else { - typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass); - } - } else { - typeHandlerRegistry.register(typeHandlerClass); - } - } - } - } - } - - private void mapperElement(XNode parent) throws Exception { - if (parent != null) { - for (XNode child : parent.getChildren()) { - if ("package".equals(child.getName())) { - String mapperPackage = child.getStringAttribute("name"); - configuration.addMappers(mapperPackage); - } else { - String resource = child.getStringAttribute("resource"); - String url = child.getStringAttribute("url"); - String mapperClass = child.getStringAttribute("class"); - if (resource != null && url == null && mapperClass == null) { - ErrorContext.instance().resource(resource); - try(InputStream inputStream = Resources.getResourceAsStream(resource)) { - XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments()); - mapperParser.parse(); - } - } else if (resource == null && url != null && mapperClass == null) { - ErrorContext.instance().resource(url); - try(InputStream inputStream = Resources.getUrlAsStream(url)){ - XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments()); - mapperParser.parse(); - } - } else if (resource == null && url == null && mapperClass != null) { - Class mapperInterface = Resources.classForName(mapperClass); - configuration.addMapper(mapperInterface); - } else { - throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one."); - } - } - } - } - } - - private boolean isSpecifiedEnvironment(String id) { - if (environment == null) { - throw new BuilderException("No environment specified."); - } - if (id == null) { - throw new BuilderException("Environment requires an id attribute."); - } - return environment.equals(id); - } - -}