MybatisSqlSessionFactoryBean.java 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596
  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.spring;
  17. import static org.springframework.util.Assert.notNull;
  18. import static org.springframework.util.Assert.state;
  19. import static org.springframework.util.ObjectUtils.isEmpty;
  20. import static org.springframework.util.StringUtils.hasLength;
  21. import static org.springframework.util.StringUtils.tokenizeToStringArray;
  22. import java.io.IOException;
  23. import java.sql.SQLException;
  24. import java.util.Properties;
  25. import javax.sql.DataSource;
  26. import org.apache.ibatis.builder.xml.XMLMapperBuilder;
  27. import org.apache.ibatis.cache.Cache;
  28. import org.apache.ibatis.executor.ErrorContext;
  29. import org.apache.ibatis.io.VFS;
  30. import org.apache.ibatis.logging.Log;
  31. import org.apache.ibatis.logging.LogFactory;
  32. import org.apache.ibatis.mapping.DatabaseIdProvider;
  33. import org.apache.ibatis.mapping.Environment;
  34. import org.apache.ibatis.plugin.Interceptor;
  35. import org.apache.ibatis.reflection.factory.ObjectFactory;
  36. import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
  37. import org.apache.ibatis.session.Configuration;
  38. import org.apache.ibatis.session.SqlSessionFactory;
  39. import org.apache.ibatis.session.SqlSessionFactoryBuilder;
  40. import org.apache.ibatis.transaction.TransactionFactory;
  41. import org.apache.ibatis.type.TypeHandler;
  42. import org.mybatis.spring.SqlSessionFactoryBean;
  43. import org.mybatis.spring.transaction.SpringManagedTransactionFactory;
  44. import org.springframework.beans.factory.FactoryBean;
  45. import org.springframework.beans.factory.InitializingBean;
  46. import org.springframework.context.ApplicationEvent;
  47. import org.springframework.context.ApplicationListener;
  48. import org.springframework.context.ConfigurableApplicationContext;
  49. import org.springframework.context.event.ContextRefreshedEvent;
  50. import org.springframework.core.NestedIOException;
  51. import org.springframework.core.io.Resource;
  52. import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;
  53. import com.baomidou.mybatisplus.MybatisConfiguration;
  54. import com.baomidou.mybatisplus.MybatisXMLConfigBuilder;
  55. import com.baomidou.mybatisplus.entity.GlobalConfiguration;
  56. import com.baomidou.mybatisplus.exceptions.MybatisPlusException;
  57. import com.baomidou.mybatisplus.mapper.SqlRunner;
  58. import com.baomidou.mybatisplus.toolkit.PackageHelper;
  59. /**
  60. * <p>
  61. * 拷贝类 org.mybatis.spring.SqlSessionFactoryBean 修改方法 buildSqlSessionFactory()
  62. * 加载自定义 MybatisXmlConfigBuilder
  63. * </p>
  64. *
  65. * @author hubin
  66. * @Date 2017-01-04
  67. */
  68. public class MybatisSqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
  69. private static final Log LOGGER = LogFactory.getLog(SqlSessionFactoryBean.class);
  70. private Resource configLocation;
  71. private Configuration configuration;
  72. private Resource[] mapperLocations;
  73. private DataSource dataSource;
  74. private TransactionFactory transactionFactory;
  75. private Properties configurationProperties;
  76. private SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
  77. private SqlSessionFactory sqlSessionFactory;
  78. //EnvironmentAware requires spring 3.1
  79. private String environment = MybatisSqlSessionFactoryBean.class.getSimpleName();
  80. private boolean failFast;
  81. private Interceptor[] plugins;
  82. private TypeHandler<?>[] typeHandlers;
  83. private String typeHandlersPackage;
  84. private Class<?>[] typeAliases;
  85. private String typeAliasesPackage;
  86. private Class<?> typeAliasesSuperType;
  87. //issue #19. No default provider.
  88. private DatabaseIdProvider databaseIdProvider;
  89. private Class<? extends VFS> vfs;
  90. private Cache cache;
  91. private ObjectFactory objectFactory;
  92. private ObjectWrapperFactory objectWrapperFactory;
  93. private GlobalConfiguration globalConfig = GlobalConfiguration.defaults();
  94. // TODO 注入全局配置
  95. public void setGlobalConfig(GlobalConfiguration globalConfig) {
  96. this.globalConfig = globalConfig;
  97. }
  98. /**
  99. * Sets the ObjectFactory.
  100. *
  101. * @param objectFactory
  102. * @since 1.1.2
  103. */
  104. public void setObjectFactory(ObjectFactory objectFactory) {
  105. this.objectFactory = objectFactory;
  106. }
  107. /**
  108. * Sets the ObjectWrapperFactory.
  109. *
  110. * @param objectWrapperFactory
  111. * @since 1.1.2
  112. */
  113. public void setObjectWrapperFactory(ObjectWrapperFactory objectWrapperFactory) {
  114. this.objectWrapperFactory = objectWrapperFactory;
  115. }
  116. /**
  117. * Gets the DatabaseIdProvider
  118. *
  119. * @return
  120. * @since 1.1.0
  121. */
  122. public DatabaseIdProvider getDatabaseIdProvider() {
  123. return databaseIdProvider;
  124. }
  125. /**
  126. * Sets the DatabaseIdProvider.
  127. * As of version 1.2.2 this variable is not initialized by default.
  128. *
  129. * @param databaseIdProvider
  130. * @since 1.1.0
  131. */
  132. public void setDatabaseIdProvider(DatabaseIdProvider databaseIdProvider) {
  133. this.databaseIdProvider = databaseIdProvider;
  134. }
  135. public Class<? extends VFS> getVfs() {
  136. return this.vfs;
  137. }
  138. public void setVfs(Class<? extends VFS> vfs) {
  139. this.vfs = vfs;
  140. }
  141. public Cache getCache() {
  142. return this.cache;
  143. }
  144. public void setCache(Cache cache) {
  145. this.cache = cache;
  146. }
  147. /**
  148. * Mybatis plugin list.
  149. *
  150. * @param plugins list of plugins
  151. * @since 1.0.1
  152. */
  153. public void setPlugins(Interceptor[] plugins) {
  154. this.plugins = plugins;
  155. }
  156. /**
  157. * Packages to search for type aliases.
  158. *
  159. * @param typeAliasesPackage package to scan for domain objects
  160. * @since 1.0.1
  161. */
  162. public void setTypeAliasesPackage(String typeAliasesPackage) {
  163. this.typeAliasesPackage = typeAliasesPackage;
  164. }
  165. /**
  166. * Super class which domain objects have to extend to have a type alias created.
  167. * No effect if there is no package to scan configured.
  168. *
  169. * @param typeAliasesSuperType super class for domain objects
  170. * @since 1.1.2
  171. */
  172. public void setTypeAliasesSuperType(Class<?> typeAliasesSuperType) {
  173. this.typeAliasesSuperType = typeAliasesSuperType;
  174. }
  175. /**
  176. * Packages to search for type handlers.
  177. *
  178. * @param typeHandlersPackage package to scan for type handlers
  179. * @since 1.0.1
  180. */
  181. public void setTypeHandlersPackage(String typeHandlersPackage) {
  182. this.typeHandlersPackage = typeHandlersPackage;
  183. }
  184. /**
  185. * Set type handlers. They must be annotated with {@code MappedTypes} and optionally with {@code MappedJdbcTypes}
  186. *
  187. * @param typeHandlers Type handler list
  188. * @since 1.0.1
  189. */
  190. public void setTypeHandlers(TypeHandler<?>[] typeHandlers) {
  191. this.typeHandlers = typeHandlers;
  192. }
  193. /**
  194. * List of type aliases to register. They can be annotated with {@code Alias}
  195. *
  196. * @param typeAliases Type aliases list
  197. * @since 1.0.1
  198. */
  199. public void setTypeAliases(Class<?>[] typeAliases) {
  200. this.typeAliases = typeAliases;
  201. }
  202. /**
  203. * If true, a final check is done on Configuration to assure that all mapped
  204. * statements are fully loaded and there is no one still pending to resolve
  205. * includes. Defaults to false.
  206. *
  207. * @param failFast enable failFast
  208. * @since 1.0.1
  209. */
  210. public void setFailFast(boolean failFast) {
  211. this.failFast = failFast;
  212. }
  213. /**
  214. * Set the location of the MyBatis {@code SqlSessionFactory} config file. A typical value is
  215. * "WEB-INF/mybatis-configuration.xml".
  216. */
  217. public void setConfigLocation(Resource configLocation) {
  218. this.configLocation = configLocation;
  219. }
  220. /**
  221. * Set a customized MyBatis configuration.
  222. *
  223. * @param configuration MyBatis configuration
  224. * @since 1.3.0
  225. */
  226. public void setConfiguration(Configuration configuration) {
  227. this.configuration = configuration;
  228. }
  229. /**
  230. * Set locations of MyBatis mapper files that are going to be merged into the {@code SqlSessionFactory}
  231. * configuration at runtime.
  232. * <p>
  233. * This is an alternative to specifying "&lt;sqlmapper&gt;" entries in an MyBatis config file.
  234. * This property being based on Spring's resource abstraction also allows for specifying
  235. * resource patterns here: e.g. "classpath*:sqlmap/*-mapper.xml".
  236. */
  237. public void setMapperLocations(Resource[] mapperLocations) {
  238. this.mapperLocations = mapperLocations;
  239. }
  240. /**
  241. * Set optional properties to be passed into the SqlSession configuration, as alternative to a
  242. * {@code &lt;properties&gt;} tag in the configuration xml file. This will be used to
  243. * resolve placeholders in the config file.
  244. */
  245. public void setConfigurationProperties(Properties sqlSessionFactoryProperties) {
  246. this.configurationProperties = sqlSessionFactoryProperties;
  247. }
  248. /**
  249. * Set the JDBC {@code DataSource} that this instance should manage transactions for. The {@code DataSource}
  250. * should match the one used by the {@code SqlSessionFactory}: for example, you could specify the same
  251. * JNDI DataSource for both.
  252. * <p>
  253. * A transactional JDBC {@code Connection} for this {@code DataSource} will be provided to application code
  254. * accessing this {@code DataSource} directly via {@code DataSourceUtils} or {@code DataSourceTransactionManager}.
  255. * <p>
  256. * The {@code DataSource} specified here should be the target {@code DataSource} to manage transactions for, not
  257. * a {@code TransactionAwareDataSourceProxy}. Only data access code may work with
  258. * {@code TransactionAwareDataSourceProxy}, while the transaction manager needs to work on the
  259. * underlying target {@code DataSource}. If there's nevertheless a {@code TransactionAwareDataSourceProxy}
  260. * passed in, it will be unwrapped to extract its target {@code DataSource}.
  261. */
  262. public void setDataSource(DataSource dataSource) {
  263. if (dataSource instanceof TransactionAwareDataSourceProxy) {
  264. // If we got a TransactionAwareDataSourceProxy, we need to perform
  265. // transactions for its underlying target DataSource, else data
  266. // access code won't see properly exposed transactions (i.e.
  267. // transactions for the target DataSource).
  268. this.dataSource = ((TransactionAwareDataSourceProxy) dataSource).getTargetDataSource();
  269. } else {
  270. this.dataSource = dataSource;
  271. }
  272. }
  273. /**
  274. * Sets the {@code SqlSessionFactoryBuilder} to use when creating the {@code SqlSessionFactory}.
  275. * <p>
  276. * This is mainly meant for testing so that mock SqlSessionFactory classes can be injected. By
  277. * default, {@code SqlSessionFactoryBuilder} creates {@code DefaultSqlSessionFactory} instances.
  278. */
  279. public void setSqlSessionFactoryBuilder(SqlSessionFactoryBuilder sqlSessionFactoryBuilder) {
  280. this.sqlSessionFactoryBuilder = sqlSessionFactoryBuilder;
  281. }
  282. /**
  283. * Set the MyBatis TransactionFactory to use. Default is {@code SpringManagedTransactionFactory}
  284. * <p>
  285. * The default {@code SpringManagedTransactionFactory} should be appropriate for all cases:
  286. * be it Spring transaction management, EJB CMT or plain JTA. If there is no active transaction,
  287. * SqlSession operations will execute SQL statements non-transactionally.
  288. * <p>
  289. * <b>It is strongly recommended to use the default {@code TransactionFactory}.</b> If not used, any
  290. * attempt at getting an SqlSession through Spring's MyBatis framework will throw an exception if
  291. * a transaction is active.
  292. *
  293. * @param transactionFactory the MyBatis TransactionFactory
  294. * @see SpringManagedTransactionFactory
  295. */
  296. public void setTransactionFactory(TransactionFactory transactionFactory) {
  297. this.transactionFactory = transactionFactory;
  298. }
  299. /**
  300. * <b>NOTE:</b> This class <em>overrides</em> any {@code Environment} you have set in the MyBatis
  301. * config file. This is used only as a placeholder name. The default value is
  302. * {@code SqlSessionFactoryBean.class.getSimpleName()}.
  303. *
  304. * @param environment the environment name
  305. */
  306. public void setEnvironment(String environment) {
  307. this.environment = environment;
  308. }
  309. /**
  310. * {@inheritDoc}
  311. */
  312. @Override
  313. public void afterPropertiesSet() throws Exception {
  314. notNull(dataSource, "Property 'dataSource' is required");
  315. notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
  316. state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null),
  317. "Property 'configuration' and 'configLocation' can not specified with together");
  318. this.sqlSessionFactory = buildSqlSessionFactory();
  319. }
  320. /**
  321. * Build a {@code SqlSessionFactory} instance.
  322. * <p>
  323. * The default implementation uses the standard MyBatis {@code XMLConfigBuilder} API to build a
  324. * {@code SqlSessionFactory} instance based on an Reader.
  325. * Since 1.3.0, it can be specified a {@link Configuration} instance directly(without config file).
  326. *
  327. * @return SqlSessionFactory
  328. * @throws IOException if loading the config file failed
  329. */
  330. protected SqlSessionFactory buildSqlSessionFactory() throws Exception {
  331. Configuration configuration;
  332. // TODO 加载自定义 MybatisXmlConfigBuilder
  333. MybatisXMLConfigBuilder xmlConfigBuilder = null;
  334. if (this.configuration != null) {
  335. configuration = this.configuration;
  336. if (configuration.getVariables() == null) {
  337. configuration.setVariables(this.configurationProperties);
  338. } else if (this.configurationProperties != null) {
  339. configuration.getVariables().putAll(this.configurationProperties);
  340. }
  341. } else if (this.configLocation != null) {
  342. xmlConfigBuilder = new MybatisXMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
  343. configuration = xmlConfigBuilder.getConfiguration();
  344. } else {
  345. if (LOGGER.isDebugEnabled()) {
  346. LOGGER.debug("Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration");
  347. }
  348. // TODO 使用自定义配置
  349. configuration = new MybatisConfiguration();
  350. if (this.configurationProperties != null) {
  351. configuration.setVariables(this.configurationProperties);
  352. }
  353. }
  354. if (this.objectFactory != null) {
  355. configuration.setObjectFactory(this.objectFactory);
  356. }
  357. if (this.objectWrapperFactory != null) {
  358. configuration.setObjectWrapperFactory(this.objectWrapperFactory);
  359. }
  360. if (this.vfs != null) {
  361. configuration.setVfsImpl(this.vfs);
  362. }
  363. if (hasLength(this.typeAliasesPackage)) {
  364. // TODO 支持自定义通配符
  365. String[] typeAliasPackageArray;
  366. if (typeAliasesPackage.contains("*")) {
  367. typeAliasPackageArray = PackageHelper.convertTypeAliasesPackage(typeAliasesPackage);
  368. } else {
  369. typeAliasPackageArray = tokenizeToStringArray(this.typeAliasesPackage,
  370. ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
  371. }
  372. if (typeAliasPackageArray == null) {
  373. throw new MybatisPlusException("not find typeAliasesPackage:" + typeAliasesPackage);
  374. }
  375. for (String packageToScan : typeAliasPackageArray) {
  376. configuration.getTypeAliasRegistry().registerAliases(packageToScan,
  377. typeAliasesSuperType == null ? Object.class : typeAliasesSuperType);
  378. if (LOGGER.isDebugEnabled()) {
  379. LOGGER.debug("Scanned package: '" + packageToScan + "' for aliases");
  380. }
  381. }
  382. }
  383. if (!isEmpty(this.typeAliases)) {
  384. for (Class<?> typeAlias : this.typeAliases) {
  385. configuration.getTypeAliasRegistry().registerAlias(typeAlias);
  386. if (LOGGER.isDebugEnabled()) {
  387. LOGGER.debug("Registered type alias: '" + typeAlias + "'");
  388. }
  389. }
  390. }
  391. if (!isEmpty(this.plugins)) {
  392. for (Interceptor plugin : this.plugins) {
  393. configuration.addInterceptor(plugin);
  394. if (LOGGER.isDebugEnabled()) {
  395. LOGGER.debug("Registered plugin: '" + plugin + "'");
  396. }
  397. }
  398. }
  399. if (hasLength(this.typeHandlersPackage)) {
  400. String[] typeHandlersPackageArray = tokenizeToStringArray(this.typeHandlersPackage,
  401. ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
  402. for (String packageToScan : typeHandlersPackageArray) {
  403. configuration.getTypeHandlerRegistry().register(packageToScan);
  404. if (LOGGER.isDebugEnabled()) {
  405. LOGGER.debug("Scanned package: '" + packageToScan + "' for type handlers");
  406. }
  407. }
  408. }
  409. if (!isEmpty(this.typeHandlers)) {
  410. for (TypeHandler<?> typeHandler : this.typeHandlers) {
  411. configuration.getTypeHandlerRegistry().register(typeHandler);
  412. if (LOGGER.isDebugEnabled()) {
  413. LOGGER.debug("Registered type handler: '" + typeHandler + "'");
  414. }
  415. }
  416. }
  417. if (this.databaseIdProvider != null) {//fix #64 set databaseId before parse mapper xmls
  418. try {
  419. configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
  420. } catch (SQLException e) {
  421. throw new NestedIOException("Failed getting a databaseId", e);
  422. }
  423. }
  424. if (this.cache != null) {
  425. configuration.addCache(this.cache);
  426. }
  427. if (xmlConfigBuilder != null) {
  428. try {
  429. xmlConfigBuilder.parse();
  430. if (LOGGER.isDebugEnabled()) {
  431. LOGGER.debug("Parsed configuration file: '" + this.configLocation + "'");
  432. }
  433. } catch (Exception ex) {
  434. throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex);
  435. } finally {
  436. ErrorContext.instance().reset();
  437. }
  438. }
  439. if (this.transactionFactory == null) {
  440. this.transactionFactory = new SpringManagedTransactionFactory();
  441. }
  442. configuration.setEnvironment(new Environment(this.environment, this.transactionFactory, this.dataSource));
  443. // 设置元数据相关
  444. GlobalConfiguration.setMetaData(dataSource, globalConfig);
  445. SqlSessionFactory sqlSessionFactory = this.sqlSessionFactoryBuilder.build(configuration);
  446. // TODO SqlRunner
  447. SqlRunner.FACTORY = sqlSessionFactory;
  448. // TODO 缓存 sqlSessionFactory
  449. globalConfig.setSqlSessionFactory(sqlSessionFactory);
  450. // TODO 设置全局参数属性
  451. globalConfig.signGlobalConfig(sqlSessionFactory);
  452. if (!isEmpty(this.mapperLocations)) {
  453. if (globalConfig.isRefresh()) {
  454. //TODO 设置自动刷新配置 减少配置
  455. new MybatisMapperRefresh(sqlSessionFactory, 2,
  456. 2, true);
  457. }
  458. for (Resource mapperLocation : this.mapperLocations) {
  459. if (mapperLocation == null) {
  460. continue;
  461. }
  462. try {
  463. // TODO 这里也换了噢噢噢噢
  464. XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
  465. configuration, mapperLocation.toString(), configuration.getSqlFragments());
  466. xmlMapperBuilder.parse();
  467. } catch (Exception e) {
  468. throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
  469. } finally {
  470. ErrorContext.instance().reset();
  471. }
  472. if (LOGGER.isDebugEnabled()) {
  473. LOGGER.debug("Parsed mapper file: '" + mapperLocation + "'");
  474. }
  475. }
  476. } else {
  477. if (LOGGER.isDebugEnabled()) {
  478. LOGGER.debug("Property 'mapperLocations' was not specified or no matching resources found");
  479. }
  480. }
  481. return sqlSessionFactory;
  482. }
  483. /**
  484. * {@inheritDoc}
  485. */
  486. @Override
  487. public SqlSessionFactory getObject() throws Exception {
  488. if (this.sqlSessionFactory == null) {
  489. afterPropertiesSet();
  490. }
  491. return this.sqlSessionFactory;
  492. }
  493. /**
  494. * {@inheritDoc}
  495. */
  496. @Override
  497. public Class<? extends SqlSessionFactory> getObjectType() {
  498. return this.sqlSessionFactory == null ? SqlSessionFactory.class : this.sqlSessionFactory.getClass();
  499. }
  500. /**
  501. * {@inheritDoc}
  502. */
  503. @Override
  504. public boolean isSingleton() {
  505. return true;
  506. }
  507. /**
  508. * {@inheritDoc}
  509. */
  510. @Override
  511. public void onApplicationEvent(ApplicationEvent event) {
  512. if (failFast && event instanceof ContextRefreshedEvent) {
  513. // fail-fast -> check all statements are completed
  514. this.sqlSessionFactory.getConfiguration().getMappedStatementNames();
  515. }
  516. }
  517. }