123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440 |
- /**
- * Copyright (c) 2011-2014, hubin (jobob@qq.com).
- * <p>
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- * <p>
- * http://www.apache.org/licenses/LICENSE-2.0
- * <p>
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
- package com.baomidou.mybatisplus;
- import java.io.InputStream;
- import java.io.Reader;
- import java.util.HashSet;
- import java.util.Properties;
- import java.util.Set;
- import javax.sql.DataSource;
- 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.ResolverUtil;
- 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.AutoMappingBehavior;
- import org.apache.ibatis.session.AutoMappingUnknownColumnBehavior;
- import org.apache.ibatis.session.Configuration;
- import org.apache.ibatis.session.ExecutorType;
- import org.apache.ibatis.session.LocalCacheScope;
- import org.apache.ibatis.transaction.TransactionFactory;
- import org.apache.ibatis.type.JdbcType;
- /**
- * <p>
- * Copy from XMLConfigBuilder in Mybatis and replace default Configuration class
- * by MybatisConfiguration class
- * </p>
- *
- * @author hubin
- * @Date 2017-01-04
- */
- 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 {
- //issue #117 read properties first
- propertiesElement(root.evalNode("properties"));
- Properties settings = settingsAsProperties(root.evalNode("settings"));
- 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 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<? 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"), 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.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"), true));
- configuration.setReturnInstanceForEmptyRow(booleanValueOf(props.getProperty("returnInstanceForEmptyRow"), 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);
- }
- }
- }
- }
- }
- // TODO 这里需要优化
- private void mapperElement(XNode parent) throws Exception {
- /**
- * 定义集合 用来分类放置mybatis的Mapper与XML 按顺序依次遍历
- */
- if (parent != null) {
- //指定在classpath中的mapper文件
- Set<String> resources = new HashSet<>();
- //指向一个mapper接口
- Set<Class<?>> mapperClasses = new HashSet<>();
- setResource(parent, resources, mapperClasses);
- // 依次遍历 首先 resource 然后 mapper
- for (String resource : resources) {
- ErrorContext.instance().resource(resource);
- InputStream inputStream = Resources.getResourceAsStream(resource);
- //TODO
- XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource,
- configuration.getSqlFragments());
- mapperParser.parse();
- }
- for (Class<?> mapper : mapperClasses) {
- //TODO
- configuration.addMapper(mapper);
- }
- }
- }
- /**
- * 查找mybatis配置文件填充至Set集合
- *
- * @param parent 节点
- * @param resources
- * @param mapper
- * @throws ClassNotFoundException
- */
- private void setResource(XNode parent, Set<String> resources, Set<Class<?>> mapper) throws ClassNotFoundException {
- for (XNode child : parent.getChildren()) {
- if ("package".equals(child.getName())) {
- String mapperPackage = child.getStringAttribute("name");
- ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
- resolverUtil.find(new ResolverUtil.IsA(Object.class), mapperPackage);
- Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();
- mapper.addAll(mapperSet);
- } 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) {
- resources.add(url);
- } else if (resource == null && url == null && mapperClass != null) {
- Class<?> mapperInterface = Resources.classForName(mapperClass);
- mapper.add(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;
- }
- }
|