MybatisXMLConfigBuilder.java 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  1. /**
  2. * Copyright (c) 2011-2014, hubin (jobob@qq.com).
  3. * <p>
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. * use this file except in compliance with the License. You may obtain a copy of
  6. * the License at
  7. * <p>
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. * <p>
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations under
  14. * the License.
  15. */
  16. package com.baomidou.mybatisplus;
  17. import java.io.InputStream;
  18. import java.io.Reader;
  19. import java.util.HashSet;
  20. import java.util.Properties;
  21. import java.util.Set;
  22. import javax.sql.DataSource;
  23. import org.apache.ibatis.builder.BaseBuilder;
  24. import org.apache.ibatis.builder.BuilderException;
  25. import org.apache.ibatis.builder.xml.XMLMapperBuilder;
  26. import org.apache.ibatis.builder.xml.XMLMapperEntityResolver;
  27. import org.apache.ibatis.datasource.DataSourceFactory;
  28. import org.apache.ibatis.executor.ErrorContext;
  29. import org.apache.ibatis.executor.loader.ProxyFactory;
  30. import org.apache.ibatis.io.ResolverUtil;
  31. import org.apache.ibatis.io.Resources;
  32. import org.apache.ibatis.io.VFS;
  33. import org.apache.ibatis.logging.Log;
  34. import org.apache.ibatis.mapping.DatabaseIdProvider;
  35. import org.apache.ibatis.mapping.Environment;
  36. import org.apache.ibatis.parsing.XNode;
  37. import org.apache.ibatis.parsing.XPathParser;
  38. import org.apache.ibatis.plugin.Interceptor;
  39. import org.apache.ibatis.reflection.DefaultReflectorFactory;
  40. import org.apache.ibatis.reflection.MetaClass;
  41. import org.apache.ibatis.reflection.ReflectorFactory;
  42. import org.apache.ibatis.reflection.factory.ObjectFactory;
  43. import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
  44. import org.apache.ibatis.session.AutoMappingBehavior;
  45. import org.apache.ibatis.session.AutoMappingUnknownColumnBehavior;
  46. import org.apache.ibatis.session.Configuration;
  47. import org.apache.ibatis.session.ExecutorType;
  48. import org.apache.ibatis.session.LocalCacheScope;
  49. import org.apache.ibatis.transaction.TransactionFactory;
  50. import org.apache.ibatis.type.JdbcType;
  51. /**
  52. * <p>
  53. * Copy from XMLConfigBuilder in Mybatis and replace default Configuration class
  54. * by MybatisConfiguration class
  55. * </p>
  56. *
  57. * @author hubin
  58. * @Date 2017-01-04
  59. */
  60. public class MybatisXMLConfigBuilder extends BaseBuilder {
  61. private boolean parsed;
  62. private XPathParser parser;
  63. private String environment;
  64. private ReflectorFactory localReflectorFactory = new DefaultReflectorFactory();
  65. public MybatisXMLConfigBuilder(Reader reader) {
  66. this(reader, null, null);
  67. }
  68. public MybatisXMLConfigBuilder(Reader reader, String environment) {
  69. this(reader, environment, null);
  70. }
  71. public MybatisXMLConfigBuilder(Reader reader, String environment, Properties props) {
  72. this(new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props);
  73. }
  74. public MybatisXMLConfigBuilder(InputStream inputStream) {
  75. this(inputStream, null, null);
  76. }
  77. public MybatisXMLConfigBuilder(InputStream inputStream, String environment) {
  78. this(inputStream, environment, null);
  79. }
  80. public MybatisXMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
  81. this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
  82. }
  83. private MybatisXMLConfigBuilder(XPathParser parser, String environment, Properties props) {
  84. //TODO 自定义 Configuration
  85. super(new MybatisConfiguration());
  86. ErrorContext.instance().resource("SQL Mapper Configuration");
  87. this.configuration.setVariables(props);
  88. this.parsed = false;
  89. this.environment = environment;
  90. this.parser = parser;
  91. }
  92. public Configuration parse() {
  93. if (parsed) {
  94. throw new BuilderException("Each XMLConfigBuilder can only be used once.");
  95. }
  96. parsed = true;
  97. parseConfiguration(parser.evalNode("/configuration"));
  98. return configuration;
  99. }
  100. private void parseConfiguration(XNode root) {
  101. try {
  102. //issue #117 read properties first
  103. propertiesElement(root.evalNode("properties"));
  104. Properties settings = settingsAsProperties(root.evalNode("settings"));
  105. loadCustomVfs(settings);
  106. typeAliasesElement(root.evalNode("typeAliases"));
  107. pluginElement(root.evalNode("plugins"));
  108. objectFactoryElement(root.evalNode("objectFactory"));
  109. objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
  110. reflectorFactoryElement(root.evalNode("reflectorFactory"));
  111. settingsElement(settings);
  112. // read it after objectFactory and objectWrapperFactory issue #631
  113. environmentsElement(root.evalNode("environments"));
  114. databaseIdProviderElement(root.evalNode("databaseIdProvider"));
  115. typeHandlerElement(root.evalNode("typeHandlers"));
  116. mapperElement(root.evalNode("mappers"));
  117. } catch (Exception e) {
  118. throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
  119. }
  120. }
  121. private Properties settingsAsProperties(XNode context) {
  122. if (context == null) {
  123. return new Properties();
  124. }
  125. Properties props = context.getChildrenAsProperties();
  126. // Check that all settings are known to the configuration class
  127. MetaClass metaConfig = MetaClass.forClass(Configuration.class, localReflectorFactory);
  128. for (Object key : props.keySet()) {
  129. if (!metaConfig.hasSetter(String.valueOf(key))) {
  130. throw new BuilderException("The setting " + key + " is not known. Make sure you spelled it correctly (case sensitive).");
  131. }
  132. }
  133. return props;
  134. }
  135. private void loadCustomVfs(Properties props) throws ClassNotFoundException {
  136. String value = props.getProperty("vfsImpl");
  137. if (value != null) {
  138. String[] clazzes = value.split(",");
  139. for (String clazz : clazzes) {
  140. if (!clazz.isEmpty()) {
  141. @SuppressWarnings("unchecked")
  142. Class<? extends VFS> vfsImpl = (Class<? extends VFS>) Resources.classForName(clazz);
  143. configuration.setVfsImpl(vfsImpl);
  144. }
  145. }
  146. }
  147. }
  148. private void typeAliasesElement(XNode parent) {
  149. if (parent != null) {
  150. for (XNode child : parent.getChildren()) {
  151. if ("package".equals(child.getName())) {
  152. String typeAliasPackage = child.getStringAttribute("name");
  153. configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
  154. } else {
  155. String alias = child.getStringAttribute("alias");
  156. String type = child.getStringAttribute("type");
  157. try {
  158. Class<?> clazz = Resources.classForName(type);
  159. if (alias == null) {
  160. typeAliasRegistry.registerAlias(clazz);
  161. } else {
  162. typeAliasRegistry.registerAlias(alias, clazz);
  163. }
  164. } catch (ClassNotFoundException e) {
  165. throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);
  166. }
  167. }
  168. }
  169. }
  170. }
  171. private void pluginElement(XNode parent) throws Exception {
  172. if (parent != null) {
  173. for (XNode child : parent.getChildren()) {
  174. String interceptor = child.getStringAttribute("interceptor");
  175. Properties properties = child.getChildrenAsProperties();
  176. Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
  177. interceptorInstance.setProperties(properties);
  178. configuration.addInterceptor(interceptorInstance);
  179. }
  180. }
  181. }
  182. private void objectFactoryElement(XNode context) throws Exception {
  183. if (context != null) {
  184. String type = context.getStringAttribute("type");
  185. Properties properties = context.getChildrenAsProperties();
  186. ObjectFactory factory = (ObjectFactory) resolveClass(type).newInstance();
  187. factory.setProperties(properties);
  188. configuration.setObjectFactory(factory);
  189. }
  190. }
  191. private void objectWrapperFactoryElement(XNode context) throws Exception {
  192. if (context != null) {
  193. String type = context.getStringAttribute("type");
  194. ObjectWrapperFactory factory = (ObjectWrapperFactory) resolveClass(type).newInstance();
  195. configuration.setObjectWrapperFactory(factory);
  196. }
  197. }
  198. private void reflectorFactoryElement(XNode context) throws Exception {
  199. if (context != null) {
  200. String type = context.getStringAttribute("type");
  201. ReflectorFactory factory = (ReflectorFactory) resolveClass(type).newInstance();
  202. configuration.setReflectorFactory(factory);
  203. }
  204. }
  205. private void propertiesElement(XNode context) throws Exception {
  206. if (context != null) {
  207. Properties defaults = context.getChildrenAsProperties();
  208. String resource = context.getStringAttribute("resource");
  209. String url = context.getStringAttribute("url");
  210. if (resource != null && url != null) {
  211. throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference. Please specify one or the other.");
  212. }
  213. if (resource != null) {
  214. defaults.putAll(Resources.getResourceAsProperties(resource));
  215. } else if (url != null) {
  216. defaults.putAll(Resources.getUrlAsProperties(url));
  217. }
  218. Properties vars = configuration.getVariables();
  219. if (vars != null) {
  220. defaults.putAll(vars);
  221. }
  222. parser.setVariables(defaults);
  223. configuration.setVariables(defaults);
  224. }
  225. }
  226. private void settingsElement(Properties props) throws Exception {
  227. configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL")));
  228. configuration.setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior.valueOf(props.getProperty("autoMappingUnknownColumnBehavior", "NONE")));
  229. configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));
  230. configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory")));
  231. configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false));
  232. configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading"), false));
  233. configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true));
  234. configuration.setUseColumnLabel(booleanValueOf(props.getProperty("useColumnLabel"), true));
  235. configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys"), false));
  236. configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE")));
  237. configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null));
  238. configuration.setDefaultFetchSize(integerValueOf(props.getProperty("defaultFetchSize"), null));
  239. configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false));
  240. configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false));
  241. configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION")));
  242. configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER")));
  243. configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString"));
  244. configuration.setSafeResultHandlerEnabled(booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true));
  245. configuration.setDefaultScriptingLanguage(resolveClass(props.getProperty("defaultScriptingLanguage")));
  246. configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false));
  247. configuration.setUseActualParamName(booleanValueOf(props.getProperty("useActualParamName"), true));
  248. configuration.setReturnInstanceForEmptyRow(booleanValueOf(props.getProperty("returnInstanceForEmptyRow"), false));
  249. configuration.setLogPrefix(props.getProperty("logPrefix"));
  250. @SuppressWarnings("unchecked")
  251. Class<? extends Log> logImpl = (Class<? extends Log>) resolveClass(props.getProperty("logImpl"));
  252. configuration.setLogImpl(logImpl);
  253. configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory")));
  254. }
  255. private void environmentsElement(XNode context) throws Exception {
  256. if (context != null) {
  257. if (environment == null) {
  258. environment = context.getStringAttribute("default");
  259. }
  260. for (XNode child : context.getChildren()) {
  261. String id = child.getStringAttribute("id");
  262. if (isSpecifiedEnvironment(id)) {
  263. TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
  264. DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
  265. DataSource dataSource = dsFactory.getDataSource();
  266. Environment.Builder environmentBuilder = new Environment.Builder(id)
  267. .transactionFactory(txFactory)
  268. .dataSource(dataSource);
  269. configuration.setEnvironment(environmentBuilder.build());
  270. }
  271. }
  272. }
  273. }
  274. private void databaseIdProviderElement(XNode context) throws Exception {
  275. DatabaseIdProvider databaseIdProvider = null;
  276. if (context != null) {
  277. String type = context.getStringAttribute("type");
  278. // awful patch to keep backward compatibility
  279. if ("VENDOR".equals(type)) {
  280. type = "DB_VENDOR";
  281. }
  282. Properties properties = context.getChildrenAsProperties();
  283. databaseIdProvider = (DatabaseIdProvider) resolveClass(type).newInstance();
  284. databaseIdProvider.setProperties(properties);
  285. }
  286. Environment environment = configuration.getEnvironment();
  287. if (environment != null && databaseIdProvider != null) {
  288. String databaseId = databaseIdProvider.getDatabaseId(environment.getDataSource());
  289. configuration.setDatabaseId(databaseId);
  290. }
  291. }
  292. private TransactionFactory transactionManagerElement(XNode context) throws Exception {
  293. if (context != null) {
  294. String type = context.getStringAttribute("type");
  295. Properties props = context.getChildrenAsProperties();
  296. TransactionFactory factory = (TransactionFactory) resolveClass(type).newInstance();
  297. factory.setProperties(props);
  298. return factory;
  299. }
  300. throw new BuilderException("Environment declaration requires a TransactionFactory.");
  301. }
  302. private DataSourceFactory dataSourceElement(XNode context) throws Exception {
  303. if (context != null) {
  304. String type = context.getStringAttribute("type");
  305. Properties props = context.getChildrenAsProperties();
  306. DataSourceFactory factory = (DataSourceFactory) resolveClass(type).newInstance();
  307. factory.setProperties(props);
  308. return factory;
  309. }
  310. throw new BuilderException("Environment declaration requires a DataSourceFactory.");
  311. }
  312. private void typeHandlerElement(XNode parent) throws Exception {
  313. if (parent != null) {
  314. for (XNode child : parent.getChildren()) {
  315. if ("package".equals(child.getName())) {
  316. String typeHandlerPackage = child.getStringAttribute("name");
  317. typeHandlerRegistry.register(typeHandlerPackage);
  318. } else {
  319. String javaTypeName = child.getStringAttribute("javaType");
  320. String jdbcTypeName = child.getStringAttribute("jdbcType");
  321. String handlerTypeName = child.getStringAttribute("handler");
  322. Class<?> javaTypeClass = resolveClass(javaTypeName);
  323. JdbcType jdbcType = resolveJdbcType(jdbcTypeName);
  324. Class<?> typeHandlerClass = resolveClass(handlerTypeName);
  325. if (javaTypeClass != null) {
  326. if (jdbcType == null) {
  327. typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);
  328. } else {
  329. typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass);
  330. }
  331. } else {
  332. typeHandlerRegistry.register(typeHandlerClass);
  333. }
  334. }
  335. }
  336. }
  337. }
  338. // TODO 这里需要优化
  339. private void mapperElement(XNode parent) throws Exception {
  340. /**
  341. * 定义集合 用来分类放置mybatis的Mapper与XML 按顺序依次遍历
  342. */
  343. if (parent != null) {
  344. //指定在classpath中的mapper文件
  345. Set<String> resources = new HashSet<>();
  346. //指向一个mapper接口
  347. Set<Class<?>> mapperClasses = new HashSet<>();
  348. setResource(parent, resources, mapperClasses);
  349. // 依次遍历 首先 resource 然后 mapper
  350. for (String resource : resources) {
  351. ErrorContext.instance().resource(resource);
  352. InputStream inputStream = Resources.getResourceAsStream(resource);
  353. //TODO
  354. XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource,
  355. configuration.getSqlFragments());
  356. mapperParser.parse();
  357. }
  358. for (Class<?> mapper : mapperClasses) {
  359. //TODO
  360. configuration.addMapper(mapper);
  361. }
  362. }
  363. }
  364. /**
  365. * 查找mybatis配置文件填充至Set集合
  366. *
  367. * @param parent 节点
  368. * @param resources
  369. * @param mapper
  370. * @throws ClassNotFoundException
  371. */
  372. private void setResource(XNode parent, Set<String> resources, Set<Class<?>> mapper) throws ClassNotFoundException {
  373. for (XNode child : parent.getChildren()) {
  374. if ("package".equals(child.getName())) {
  375. String mapperPackage = child.getStringAttribute("name");
  376. ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
  377. resolverUtil.find(new ResolverUtil.IsA(Object.class), mapperPackage);
  378. Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();
  379. mapper.addAll(mapperSet);
  380. } else {
  381. String resource = child.getStringAttribute("resource");
  382. String url = child.getStringAttribute("url");
  383. String mapperClass = child.getStringAttribute("class");
  384. if (resource != null && url == null && mapperClass == null) {
  385. resources.add(resource);
  386. } else if (resource == null && url != null && mapperClass == null) {
  387. resources.add(url);
  388. } else if (resource == null && url == null && mapperClass != null) {
  389. Class<?> mapperInterface = Resources.classForName(mapperClass);
  390. mapper.add(mapperInterface);
  391. } else {
  392. throw new BuilderException(
  393. "A mapper element may only specify a url, resource or class, but not more than one.");
  394. }
  395. }
  396. }
  397. }
  398. private boolean isSpecifiedEnvironment(String id) {
  399. if (environment == null) {
  400. throw new BuilderException("No environment specified.");
  401. } else if (id == null) {
  402. throw new BuilderException("Environment requires an id attribute.");
  403. } else if (environment.equals(id)) {
  404. return true;
  405. }
  406. return false;
  407. }
  408. }