|
@@ -1,12 +1,12 @@
|
|
/**
|
|
/**
|
|
* Copyright (c) 2011-2014, hubin (jobob@qq.com).
|
|
* Copyright (c) 2011-2014, hubin (jobob@qq.com).
|
|
- *
|
|
|
|
|
|
+ * <p>
|
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
|
* 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
|
|
* use this file except in compliance with the License. You may obtain a copy of
|
|
* the License at
|
|
* the License at
|
|
- *
|
|
|
|
|
|
+ * <p>
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
- *
|
|
|
|
|
|
+ * <p>
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
@@ -45,6 +45,8 @@ import org.apache.ibatis.type.JdbcType;
|
|
import javax.sql.DataSource;
|
|
import javax.sql.DataSource;
|
|
import java.io.InputStream;
|
|
import java.io.InputStream;
|
|
import java.io.Reader;
|
|
import java.io.Reader;
|
|
|
|
+import java.util.ArrayList;
|
|
|
|
+import java.util.List;
|
|
import java.util.Properties;
|
|
import java.util.Properties;
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -58,356 +60,371 @@ import java.util.Properties;
|
|
*/
|
|
*/
|
|
public class MybatisXMLConfigBuilder extends BaseBuilder {
|
|
public class MybatisXMLConfigBuilder extends BaseBuilder {
|
|
|
|
|
|
- private boolean parsed;
|
|
|
|
- private XPathParser parser;
|
|
|
|
- private String environment;
|
|
|
|
- private ReflectorFactory localReflectorFactory = new DefaultReflectorFactory();
|
|
|
|
-
|
|
|
|
- public MybatisXMLConfigBuilder(Reader reader) {
|
|
|
|
- this(reader, null, null);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- public MybatisXMLConfigBuilder(Reader reader, String environment) {
|
|
|
|
- this(reader, environment, null);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- public MybatisXMLConfigBuilder(Reader reader, String environment, Properties props) {
|
|
|
|
- this(new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- public MybatisXMLConfigBuilder(InputStream inputStream) {
|
|
|
|
- this(inputStream, null, null);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- public MybatisXMLConfigBuilder(InputStream inputStream, String environment) {
|
|
|
|
- this(inputStream, environment, null);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- public MybatisXMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
|
|
|
|
- this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- private MybatisXMLConfigBuilder(XPathParser parser, String environment, Properties props) {
|
|
|
|
- //TODO 自定义 Configuration
|
|
|
|
- super(new MybatisConfiguration());
|
|
|
|
- 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 {
|
|
|
|
- Properties settings = settingsAsPropertiess(root.evalNode("settings"));
|
|
|
|
- // issue #117 read properties first
|
|
|
|
- propertiesElement(root.evalNode("properties"));
|
|
|
|
- loadCustomVfs(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 settingsAsPropertiess(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<? extends VFS> vfsImpl = (Class<? extends VFS>) Resources.classForName(clazz);
|
|
|
|
- configuration.setVfsImpl(vfsImpl);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- 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).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).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).newInstance();
|
|
|
|
- configuration.setObjectWrapperFactory(factory);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- private void reflectorFactoryElement(XNode context) throws Exception {
|
|
|
|
- if (context != null) {
|
|
|
|
- String type = context.getStringAttribute("type");
|
|
|
|
- ReflectorFactory factory = (ReflectorFactory) resolveClass(type).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) throws Exception {
|
|
|
|
- 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"), true));
|
|
|
|
- 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.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.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false));
|
|
|
|
- configuration.setUseActualParamName(booleanValueOf(props.getProperty("useActualParamName"), false));
|
|
|
|
- configuration.setLogPrefix(props.getProperty("logPrefix"));
|
|
|
|
- @SuppressWarnings("unchecked")
|
|
|
|
- Class<? extends Log> logImpl = (Class<? extends Log>) resolveClass(props.getProperty("logImpl"));
|
|
|
|
- configuration.setLogImpl(logImpl);
|
|
|
|
- configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory")));
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- 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());
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- 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).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).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).newInstance();
|
|
|
|
- factory.setProperties(props);
|
|
|
|
- return factory;
|
|
|
|
- }
|
|
|
|
- throw new BuilderException("Environment declaration requires a DataSourceFactory.");
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- private void typeHandlerElement(XNode parent) throws Exception {
|
|
|
|
- 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);
|
|
|
|
- InputStream inputStream = Resources.getResourceAsStream(resource);
|
|
|
|
- //TODO
|
|
|
|
- MybatisXMLMapperBuilder mapperParser = new MybatisXMLMapperBuilder(inputStream, configuration, resource,
|
|
|
|
- configuration.getSqlFragments());
|
|
|
|
- mapperParser.parse();
|
|
|
|
- } else if (resource == null && url != null && mapperClass == null) {
|
|
|
|
- ErrorContext.instance().resource(url);
|
|
|
|
- InputStream inputStream = Resources.getUrlAsStream(url);
|
|
|
|
- //TODO
|
|
|
|
- MybatisXMLMapperBuilder mapperParser = new MybatisXMLMapperBuilder(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.");
|
|
|
|
- } else if (id == null) {
|
|
|
|
- throw new BuilderException("Environment requires an id attribute.");
|
|
|
|
- } else if (environment.equals(id)) {
|
|
|
|
- return true;
|
|
|
|
- }
|
|
|
|
- return false;
|
|
|
|
- }
|
|
|
|
|
|
+ private boolean parsed;
|
|
|
|
+ private XPathParser parser;
|
|
|
|
+ private String environment;
|
|
|
|
+ private ReflectorFactory localReflectorFactory = new DefaultReflectorFactory();
|
|
|
|
+
|
|
|
|
+ public MybatisXMLConfigBuilder(Reader reader) {
|
|
|
|
+ this(reader, null, null);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public MybatisXMLConfigBuilder(Reader reader, String environment) {
|
|
|
|
+ this(reader, environment, null);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public MybatisXMLConfigBuilder(Reader reader, String environment, Properties props) {
|
|
|
|
+ this(new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public MybatisXMLConfigBuilder(InputStream inputStream) {
|
|
|
|
+ this(inputStream, null, null);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public MybatisXMLConfigBuilder(InputStream inputStream, String environment) {
|
|
|
|
+ this(inputStream, environment, null);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public MybatisXMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
|
|
|
|
+ this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private MybatisXMLConfigBuilder(XPathParser parser, String environment, Properties props) {
|
|
|
|
+ //TODO 自定义 Configuration
|
|
|
|
+ super(new MybatisConfiguration());
|
|
|
|
+ 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 {
|
|
|
|
+ Properties settings = settingsAsPropertiess(root.evalNode("settings"));
|
|
|
|
+ // issue #117 read properties first
|
|
|
|
+ propertiesElement(root.evalNode("properties"));
|
|
|
|
+ loadCustomVfs(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 settingsAsPropertiess(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<? extends VFS> vfsImpl = (Class<? extends VFS>) Resources.classForName(clazz);
|
|
|
|
+ configuration.setVfsImpl(vfsImpl);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ 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).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).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).newInstance();
|
|
|
|
+ configuration.setObjectWrapperFactory(factory);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private void reflectorFactoryElement(XNode context) throws Exception {
|
|
|
|
+ if (context != null) {
|
|
|
|
+ String type = context.getStringAttribute("type");
|
|
|
|
+ ReflectorFactory factory = (ReflectorFactory) resolveClass(type).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) throws Exception {
|
|
|
|
+ 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"), true));
|
|
|
|
+ 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.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.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false));
|
|
|
|
+ configuration.setUseActualParamName(booleanValueOf(props.getProperty("useActualParamName"), false));
|
|
|
|
+ configuration.setLogPrefix(props.getProperty("logPrefix"));
|
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
|
+ Class<? extends Log> logImpl = (Class<? extends Log>) resolveClass(props.getProperty("logImpl"));
|
|
|
|
+ configuration.setLogImpl(logImpl);
|
|
|
|
+ configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory")));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ 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());
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ 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).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).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).newInstance();
|
|
|
|
+ factory.setProperties(props);
|
|
|
|
+ return factory;
|
|
|
|
+ }
|
|
|
|
+ throw new BuilderException("Environment declaration requires a DataSourceFactory.");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private void typeHandlerElement(XNode parent) throws Exception {
|
|
|
|
+ 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 {
|
|
|
|
+ List<String> resources = new ArrayList<String>();
|
|
|
|
+ List<String> urls = new ArrayList<String>();
|
|
|
|
+ List<String> mapperClasses = new ArrayList<String>();
|
|
|
|
+ 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) {
|
|
|
|
+ resources.add(resource);
|
|
|
|
+ } else if (resource == null && url != null && mapperClass == null) {
|
|
|
|
+ urls.add(url);
|
|
|
|
+
|
|
|
|
+ } else if (resource == null && url == null && mapperClass != null) {
|
|
|
|
+ mapperClasses.add(mapperClass);
|
|
|
|
+
|
|
|
|
+ } else {
|
|
|
|
+ throw new BuilderException(
|
|
|
|
+ "A mapper element may only specify a url, resource or class, but not more than one.");
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ for (String url : urls) {
|
|
|
|
+ ErrorContext.instance().resource(url);
|
|
|
|
+ InputStream inputStream = Resources.getUrlAsStream(url);
|
|
|
|
+ //TODO
|
|
|
|
+ MybatisXMLMapperBuilder mapperParser = new MybatisXMLMapperBuilder(inputStream, configuration, url,
|
|
|
|
+ configuration.getSqlFragments());
|
|
|
|
+ mapperParser.parse();
|
|
|
|
+ }
|
|
|
|
+ for (String resource : resources) {
|
|
|
|
+ ErrorContext.instance().resource(resource);
|
|
|
|
+ InputStream inputStream = Resources.getResourceAsStream(resource);
|
|
|
|
+ //TODO
|
|
|
|
+ MybatisXMLMapperBuilder mapperParser = new MybatisXMLMapperBuilder(inputStream, configuration, resource,
|
|
|
|
+ configuration.getSqlFragments());
|
|
|
|
+ mapperParser.parse();
|
|
|
|
+ }
|
|
|
|
+ for (String mapper : mapperClasses) {
|
|
|
|
+ Class<?> mapperInterface = Resources.classForName(mapper);
|
|
|
|
+ configuration.addMapper(mapperInterface);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private boolean isSpecifiedEnvironment(String id) {
|
|
|
|
+ if (environment == null) {
|
|
|
|
+ throw new BuilderException("No environment specified.");
|
|
|
|
+ } else if (id == null) {
|
|
|
|
+ throw new BuilderException("Environment requires an id attribute.");
|
|
|
|
+ } else if (environment.equals(id)) {
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
|
|
}
|
|
}
|