Mybatisplus源码

  之前简单研究了Mybatis 的源码,现在简单研究下MybatisPlus 的源码。大体分析其执行过程。Mybatisplus 执行逻辑大体和mybatis一样,只是在启动过程中会生成一些默认的SQL下面研究其生成默认SQL的过程。

1. 自动配置

   查看源码按自动配置的套路,先查看AutoConfiguration和Properties文件。

1. com.baomidou.mybatisplus.spring.boot.starter.MybatisPlusProperties 属性文件

  1 package com.baomidou.mybatisplus.spring.boot.starter;
  2 
  3 import java.io.IOException;
  4 import java.util.ArrayList;
  5 import java.util.Arrays;
  6 import java.util.List;
  7 import java.util.Properties;
  8 
  9 import org.apache.ibatis.session.ExecutorType;
 10 import org.springframework.boot.context.properties.ConfigurationProperties;
 11 import org.springframework.boot.context.properties.NestedConfigurationProperty;
 12 import org.springframework.core.io.Resource;
 13 import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
 14 import org.springframework.core.io.support.ResourcePatternResolver;
 15 
 16 import com.baomidou.mybatisplus.MybatisConfiguration;
 17 
 18 /**
 19  * Configuration properties for MyBatis.
 20  *
 21  * @author Eddú Meléndez
 22  * @author Kazuki Shimizu
 23  */
 24 @ConfigurationProperties(prefix = MybatisPlusProperties.MYBATIS_PLUS_PREFIX)
 25 public class MybatisPlusProperties {
 26 
 27     public static final String MYBATIS_PLUS_PREFIX = "mybatis-plus";
 28 
 29     /**
 30      * Location of MyBatis xml config file.
 31      */
 32     private String configLocation;
 33 
 34     /**
 35      * Locations of MyBatis mapper files.
 36      */
 37     private String[] mapperLocations;
 38 
 39     /**
 40      * Packages to search type aliases. (Package delimiters are ",; 	
")
 41      */
 42     private String typeAliasesPackage;
 43 
 44     // TODO 自定义枚举包
 45     private String typeEnumsPackage;
 46 
 47     /**
 48      * Packages to search for type handlers. (Package delimiters are ",; 	
")
 49      */
 50     private String typeHandlersPackage;
 51 
 52     /**
 53      * Indicates whether perform presence check of the MyBatis xml config file.
 54      */
 55     private boolean checkConfigLocation = false;
 56 
 57     /**
 58      * Execution mode for {@link org.mybatis.spring.SqlSessionTemplate}.
 59      */
 60     private ExecutorType executorType;
 61 
 62     /**
 63      * Externalized properties for MyBatis configuration.
 64      */
 65     private Properties configurationProperties;
 66     /**
 67      * Externalized properties for MyBatis configuration.
 68      */
 69     @NestedConfigurationProperty
 70     private GlobalConfig globalConfig;
 71 
 72     /**
 73      * A Configuration object for customize default settings. If {@link #configLocation}
 74      * is specified, this property is not used.
 75      */
 76     @NestedConfigurationProperty
 77     private MybatisConfiguration configuration;
 78 
 79     /**
 80      * @since 1.1.0
 81      */
 82     public String getConfigLocation() {
 83         return this.configLocation;
 84     }
 85 
 86     /**
 87      * @since 1.1.0
 88      */
 89     public void setConfigLocation(String configLocation) {
 90         this.configLocation = configLocation;
 91     }
 92 
 93     public String[] getMapperLocations() {
 94         return this.mapperLocations;
 95     }
 96 
 97     public void setMapperLocations(String[] mapperLocations) {
 98         this.mapperLocations = mapperLocations;
 99     }
100 
101     public String getTypeHandlersPackage() {
102         return this.typeHandlersPackage;
103     }
104 
105     public void setTypeHandlersPackage(String typeHandlersPackage) {
106         this.typeHandlersPackage = typeHandlersPackage;
107     }
108 
109     public String getTypeAliasesPackage() {
110         return this.typeAliasesPackage;
111     }
112 
113     public void setTypeAliasesPackage(String typeAliasesPackage) {
114         this.typeAliasesPackage = typeAliasesPackage;
115     }
116 
117     public String getTypeEnumsPackage() {
118         return typeEnumsPackage;
119     }
120 
121     public void setTypeEnumsPackage(String typeEnumsPackage) {
122         this.typeEnumsPackage = typeEnumsPackage;
123     }
124 
125     public boolean isCheckConfigLocation() {
126         return this.checkConfigLocation;
127     }
128 
129     public void setCheckConfigLocation(boolean checkConfigLocation) {
130         this.checkConfigLocation = checkConfigLocation;
131     }
132 
133     public ExecutorType getExecutorType() {
134         return this.executorType;
135     }
136 
137     public void setExecutorType(ExecutorType executorType) {
138         this.executorType = executorType;
139     }
140 
141     /**
142      * @since 1.2.0
143      */
144     public Properties getConfigurationProperties() {
145         return configurationProperties;
146     }
147 
148     /**
149      * @since 1.2.0
150      */
151     public void setConfigurationProperties(Properties configurationProperties) {
152         this.configurationProperties = configurationProperties;
153     }
154 
155     public MybatisConfiguration getConfiguration() {
156         return configuration;
157     }
158 
159     public void setConfiguration(MybatisConfiguration configuration) {
160         this.configuration = configuration;
161     }
162 
163     public Resource[] resolveMapperLocations() {
164         ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();
165         List<Resource> resources = new ArrayList<Resource>();
166         if (this.mapperLocations != null) {
167             for (String mapperLocation : this.mapperLocations) {
168                 try {
169                     Resource[] mappers = resourceResolver.getResources(mapperLocation);
170                     resources.addAll(Arrays.asList(mappers));
171                 } catch (IOException e) {
172                     // ignore
173                 }
174             }
175         }
176         return resources.toArray(new Resource[resources.size()]);
177     }
178 
179     public GlobalConfig getGlobalConfig() {
180         return globalConfig;
181     }
182 
183     public void setGlobalConfig(GlobalConfig globalConfig) {
184         this.globalConfig = globalConfig;
185     }
186 }
View Code

  可以看到有一些别名、配置文件所在包等的配置。还有一个重要的属性 GlobalConfig, 是mybatisplus的全局配置类。源码如下:

  1 package com.baomidou.mybatisplus.spring.boot.starter;
  2 
  3 import com.baomidou.mybatisplus.entity.GlobalConfiguration;
  4 import com.baomidou.mybatisplus.incrementer.IKeyGenerator;
  5 import com.baomidou.mybatisplus.mapper.ISqlInjector;
  6 import com.baomidou.mybatisplus.mapper.MetaObjectHandler;
  7 import com.baomidou.mybatisplus.toolkit.StringUtils;
  8 
  9 /**
 10  * <p>
 11  * Mybatis全局缓存
 12  * </p>
 13  *
 14  * @author Caratacus
 15  * @since 2017-05-01
 16  */
 17 public class GlobalConfig {
 18 
 19     /**
 20      * 主键类型
 21      */
 22     private Integer idType;
 23     /**
 24      * 表前缀
 25      */
 26     private String tablePrefix;
 27     /**
 28      * 表名、字段名、是否使用下划线命名
 29      */
 30     private Boolean dbColumnUnderline;
 31     /**
 32      * SQL注入器
 33      */
 34     @Deprecated
 35     private String sqlInjector;
 36     /**
 37      * 元对象字段填充控制器
 38      */
 39     @Deprecated
 40     private String metaObjectHandler;
 41     /**
 42      * 字段验证策略
 43      */
 44     private Integer fieldStrategy;
 45     /**
 46      * 方便调试
 47      */
 48     private Boolean refreshMapper;
 49     /**
 50      * 是否大写命名
 51      */
 52     private Boolean isCapitalMode;
 53     /**
 54      * 标识符
 55      */
 56     private String identifierQuote;
 57     /**
 58      * 逻辑删除全局值
 59      */
 60     private String logicDeleteValue = null;
 61     /**
 62      * 逻辑未删除全局值
 63      */
 64     private String logicNotDeleteValue = null;
 65     /**
 66      * 表关键词 key 生成器
 67      */
 68     @Deprecated
 69     private String keyGenerator;
 70     /**
 71      * 缓存 Sql 解析初始化
 72      */
 73     private Boolean sqlParserCache;
 74 
 75     public Integer getIdType() {
 76         return idType;
 77     }
 78 
 79     public void setIdType(Integer idType) {
 80         this.idType = idType;
 81     }
 82 
 83     public String getTablePrefix() {
 84         return tablePrefix;
 85     }
 86 
 87     public void setTablePrefix(String tablePrefix) {
 88         this.tablePrefix = tablePrefix;
 89     }
 90 
 91     public Boolean getDbColumnUnderline() {
 92         return dbColumnUnderline;
 93     }
 94 
 95     public void setDbColumnUnderline(Boolean dbColumnUnderline) {
 96         this.dbColumnUnderline = dbColumnUnderline;
 97     }
 98 
 99     public String getSqlInjector() {
100         return sqlInjector;
101     }
102 
103     public void setSqlInjector(String sqlInjector) {
104         this.sqlInjector = sqlInjector;
105     }
106 
107     public String getMetaObjectHandler() {
108         return metaObjectHandler;
109     }
110 
111     public void setMetaObjectHandler(String metaObjectHandler) {
112         this.metaObjectHandler = metaObjectHandler;
113     }
114 
115     public Integer getFieldStrategy() {
116         return fieldStrategy;
117     }
118 
119     public void setFieldStrategy(Integer fieldStrategy) {
120         this.fieldStrategy = fieldStrategy;
121     }
122 
123     public Boolean getCapitalMode() {
124         return isCapitalMode;
125     }
126 
127     public void setCapitalMode(Boolean capitalMode) {
128         isCapitalMode = capitalMode;
129     }
130 
131     public String getIdentifierQuote() {
132         return identifierQuote;
133     }
134 
135     public void setIdentifierQuote(String identifierQuote) {
136         this.identifierQuote = identifierQuote;
137     }
138 
139     public Boolean getRefreshMapper() {
140         return refreshMapper;
141     }
142 
143     public void setRefreshMapper(Boolean refreshMapper) {
144         this.refreshMapper = refreshMapper;
145     }
146 
147     public String getLogicDeleteValue() {
148         return logicDeleteValue;
149     }
150 
151     public void setLogicDeleteValue(String logicDeleteValue) {
152         this.logicDeleteValue = logicDeleteValue;
153     }
154 
155     public String getLogicNotDeleteValue() {
156         return logicNotDeleteValue;
157     }
158 
159     public void setLogicNotDeleteValue(String logicNotDeleteValue) {
160         this.logicNotDeleteValue = logicNotDeleteValue;
161     }
162 
163     public String getKeyGenerator() {
164         return keyGenerator;
165     }
166 
167     public void setKeyGenerator(String keyGenerator) {
168         this.keyGenerator = keyGenerator;
169     }
170 
171     public Boolean getSqlParserCache() {
172         return sqlParserCache;
173     }
174 
175     public void setSqlParserCache(Boolean sqlParserCache) {
176         this.sqlParserCache = sqlParserCache;
177     }
178 
179     public GlobalConfiguration convertGlobalConfiguration() throws ClassNotFoundException, IllegalAccessException, InstantiationException {
180         GlobalConfiguration globalConfiguration = new GlobalConfiguration();
181         if (StringUtils.isNotEmpty(this.getIdentifierQuote())) {
182             globalConfiguration.setIdentifierQuote(this.getIdentifierQuote());
183         }
184         if (StringUtils.isNotEmpty(this.getLogicDeleteValue())) {
185             globalConfiguration.setLogicDeleteValue(this.getLogicDeleteValue());
186         }
187         if (StringUtils.isNotEmpty(this.getLogicNotDeleteValue())) {
188             globalConfiguration.setLogicNotDeleteValue(this.getLogicNotDeleteValue());
189         }
190         if (StringUtils.isNotEmpty(this.getSqlInjector())) {
191             globalConfiguration.setSqlInjector((ISqlInjector) Class.forName(this.getSqlInjector()).newInstance());
192         }
193         if (StringUtils.isNotEmpty(this.getMetaObjectHandler())) {
194             globalConfiguration.setMetaObjectHandler((MetaObjectHandler) Class.forName(this.getMetaObjectHandler()).newInstance());
195         }
196         if (StringUtils.isNotEmpty(this.getKeyGenerator())) {
197             globalConfiguration.setKeyGenerator((IKeyGenerator) Class.forName(this.getKeyGenerator()).newInstance());
198         }
199         if (StringUtils.checkValNotNull(this.getIdType())) {
200             globalConfiguration.setIdType(this.getIdType());
201         }
202         if (StringUtils.checkValNotNull(this.getTablePrefix())) {
203             globalConfiguration.setTablePrefix(this.getTablePrefix());
204         }
205         if (null != this.getDbColumnUnderline()) {
206             globalConfiguration.setDbColumnUnderline(this.getDbColumnUnderline());
207         }
208         if (StringUtils.checkValNotNull(this.getFieldStrategy())) {
209             globalConfiguration.setFieldStrategy(this.getFieldStrategy());
210         }
211         if (StringUtils.checkValNotNull(this.getRefreshMapper())) {
212             globalConfiguration.setRefresh(this.getRefreshMapper());
213         }
214         if (StringUtils.checkValNotNull(this.getCapitalMode())) {
215             globalConfiguration.setCapitalMode(this.getCapitalMode());
216         }
217         if (null != this.getSqlParserCache()) {
218             globalConfiguration.setSqlParserCache(this.getSqlParserCache());
219         }
220         return globalConfiguration;
221     }
222 
223 }
View Code

  这里面有一些全局的配置,包括表名的前缀、是否使用下换线命名方式、sql注入器等属性

2. com.baomidou.mybatisplus.spring.boot.starter.MybatisPlusAutoConfiguration 自动配置类

  1 package com.baomidou.mybatisplus.spring.boot.starter;
  2 
  3 import java.util.List;
  4 
  5 import javax.annotation.PostConstruct;
  6 import javax.sql.DataSource;
  7 
  8 import org.apache.ibatis.annotations.Mapper;
  9 import org.apache.ibatis.logging.Log;
 10 import org.apache.ibatis.logging.LogFactory;
 11 import org.apache.ibatis.mapping.DatabaseIdProvider;
 12 import org.apache.ibatis.plugin.Interceptor;
 13 import org.apache.ibatis.session.ExecutorType;
 14 import org.apache.ibatis.session.SqlSessionFactory;
 15 import org.mybatis.spring.SqlSessionTemplate;
 16 import org.mybatis.spring.mapper.ClassPathMapperScanner;
 17 import org.mybatis.spring.mapper.MapperFactoryBean;
 18 import org.springframework.beans.BeansException;
 19 import org.springframework.beans.factory.BeanFactory;
 20 import org.springframework.beans.factory.BeanFactoryAware;
 21 import org.springframework.beans.factory.ObjectProvider;
 22 import org.springframework.beans.factory.support.BeanDefinitionRegistry;
 23 import org.springframework.boot.autoconfigure.AutoConfigurationPackages;
 24 import org.springframework.boot.autoconfigure.AutoConfigureAfter;
 25 import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
 26 import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
 27 import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
 28 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
 29 import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
 30 import org.springframework.boot.context.properties.EnableConfigurationProperties;
 31 import org.springframework.context.ApplicationContext;
 32 import org.springframework.context.ResourceLoaderAware;
 33 import org.springframework.context.annotation.Bean;
 34 import org.springframework.context.annotation.Import;
 35 import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
 36 import org.springframework.core.io.Resource;
 37 import org.springframework.core.io.ResourceLoader;
 38 import org.springframework.core.type.AnnotationMetadata;
 39 import org.springframework.util.Assert;
 40 import org.springframework.util.CollectionUtils;
 41 import org.springframework.util.ObjectUtils;
 42 import org.springframework.util.StringUtils;
 43 
 44 import com.baomidou.mybatisplus.MybatisConfiguration;
 45 import com.baomidou.mybatisplus.MybatisXMLLanguageDriver;
 46 import com.baomidou.mybatisplus.entity.GlobalConfiguration;
 47 import com.baomidou.mybatisplus.incrementer.IKeyGenerator;
 48 import com.baomidou.mybatisplus.mapper.ISqlInjector;
 49 import com.baomidou.mybatisplus.mapper.MetaObjectHandler;
 50 import com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean;
 51 
 52 /**
 53  * {@link EnableAutoConfiguration Auto-Configuration} for Mybatis. Contributes a
 54  * {@link SqlSessionFactory} and a {@link SqlSessionTemplate}.
 55  * <p>
 56  * If {@link org.mybatis.spring.annotation.MapperScan} is used, or a
 57  * configuration file is specified as a property, those will be considered,
 58  * otherwise this auto-configuration will attempt to register mappers based on
 59  * the interface definitions in or under the root auto-configuration package.
 60  *
 61  * @author Eddú Meléndez
 62  * @author Josh Long
 63  * @author Kazuki Shimizu
 64  * @author Eduardo Macarrón
 65  */
 66 @SuppressWarnings("ConstantConditions")
 67 @org.springframework.context.annotation.Configuration
 68 @ConditionalOnClass({SqlSessionFactory.class, MybatisSqlSessionFactoryBean.class})
 69 @ConditionalOnBean(DataSource.class)
 70 @EnableConfigurationProperties(MybatisPlusProperties.class)
 71 @AutoConfigureAfter(DataSourceAutoConfiguration.class)
 72 public class MybatisPlusAutoConfiguration {
 73 
 74     private static final Log logger = LogFactory.getLog(MybatisPlusAutoConfiguration.class);
 75 
 76     private final MybatisPlusProperties properties;
 77 
 78     private final Interceptor[] interceptors;
 79 
 80     private final ResourceLoader resourceLoader;
 81 
 82     private final DatabaseIdProvider databaseIdProvider;
 83 
 84     private final List<ConfigurationCustomizer> configurationCustomizers;
 85 
 86     private final ApplicationContext applicationContext;
 87 
 88     public MybatisPlusAutoConfiguration(MybatisPlusProperties properties,
 89                                         ObjectProvider<Interceptor[]> interceptorsProvider,
 90                                         ResourceLoader resourceLoader,
 91                                         ObjectProvider<DatabaseIdProvider> databaseIdProvider,
 92                                         ObjectProvider<List<ConfigurationCustomizer>> configurationCustomizersProvider, ApplicationContext applicationContext) {
 93         this.properties = properties;
 94         this.interceptors = interceptorsProvider.getIfAvailable();
 95         this.resourceLoader = resourceLoader;
 96         this.databaseIdProvider = databaseIdProvider.getIfAvailable();
 97         this.configurationCustomizers = configurationCustomizersProvider.getIfAvailable();
 98         this.applicationContext = applicationContext;
 99     }
100 
101     @PostConstruct
102     public void checkConfigFileExists() {
103         if (this.properties.isCheckConfigLocation() && StringUtils.hasText(this.properties.getConfigLocation())) {
104             Resource resource = this.resourceLoader.getResource(this.properties.getConfigLocation());
105             Assert.state(resource.exists(), "Cannot find config location: " + resource
106                 + " (please add config file or check your Mybatis configuration)");
107         }
108     }
109 
110     @Bean
111     @ConditionalOnMissingBean
112     public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
113         MybatisSqlSessionFactoryBean factory = new MybatisSqlSessionFactoryBean();
114         factory.setDataSource(dataSource);
115         factory.setVfs(SpringBootVFS.class);
116         if (StringUtils.hasText(this.properties.getConfigLocation())) {
117             factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));
118         }
119         MybatisConfiguration configuration = this.properties.getConfiguration();
120         if (configuration == null && !StringUtils.hasText(this.properties.getConfigLocation())) {
121             configuration = new MybatisConfiguration();
122         }
123         if (configuration != null && !CollectionUtils.isEmpty(this.configurationCustomizers)) {
124             for (ConfigurationCustomizer customizer : this.configurationCustomizers) {
125                 customizer.customize(configuration);
126             }
127         }
128         // TODO 自定义配置
129         if (null != configuration) {
130             configuration.setDefaultScriptingLanguage(MybatisXMLLanguageDriver.class);
131         }
132         factory.setConfiguration(configuration);
133         if (this.properties.getConfigurationProperties() != null) {
134             factory.setConfigurationProperties(this.properties.getConfigurationProperties());
135         }
136         if (!ObjectUtils.isEmpty(this.interceptors)) {
137             factory.setPlugins(this.interceptors);
138         }
139         if (this.databaseIdProvider != null) {
140             factory.setDatabaseIdProvider(this.databaseIdProvider);
141         }
142         if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) {
143             factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());
144         }
145         // TODO 自定义枚举包
146         if (StringUtils.hasLength(this.properties.getTypeEnumsPackage())) {
147             factory.setTypeEnumsPackage(this.properties.getTypeEnumsPackage());
148         }
149         if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) {
150             factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());
151         }
152         if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) {
153             factory.setMapperLocations(this.properties.resolveMapperLocations());
154         }
155         GlobalConfiguration globalConfig;
156         if (!ObjectUtils.isEmpty(this.properties.getGlobalConfig())) {
157             globalConfig = this.properties.getGlobalConfig().convertGlobalConfiguration();
158         } else {
159             globalConfig = new GlobalConfiguration();
160         }
161         //注入填充器
162         if (this.applicationContext.getBeanNamesForType(MetaObjectHandler.class, false,
163             false).length > 0) {
164             MetaObjectHandler metaObjectHandler = this.applicationContext.getBean(MetaObjectHandler.class);
165             globalConfig.setMetaObjectHandler(metaObjectHandler);
166         }
167         //注入主键生成器
168         if (this.applicationContext.getBeanNamesForType(IKeyGenerator.class, false,
169             false).length > 0) {
170             IKeyGenerator keyGenerator = this.applicationContext.getBean(IKeyGenerator.class);
171             globalConfig.setKeyGenerator(keyGenerator);
172         }
173         //注入sql注入器
174         if (this.applicationContext.getBeanNamesForType(ISqlInjector.class, false,
175             false).length > 0) {
176             ISqlInjector iSqlInjector = this.applicationContext.getBean(ISqlInjector.class);
177             globalConfig.setSqlInjector(iSqlInjector);
178         }
179         factory.setGlobalConfig(globalConfig);
180         return factory.getObject();
181     }
182 
183     @Bean
184     @ConditionalOnMissingBean
185     public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
186         ExecutorType executorType = this.properties.getExecutorType();
187         if (executorType != null) {
188             return new SqlSessionTemplate(sqlSessionFactory, executorType);
189         } else {
190             return new SqlSessionTemplate(sqlSessionFactory);
191         }
192     }
193 
194     /**
195      * This will just scan the same base package as Spring Boot does. If you want
196      * more power, you can explicitly use
197      * {@link org.mybatis.spring.annotation.MapperScan} but this will get typed
198      * mappers working correctly, out-of-the-box, similar to using Spring Data JPA
199      * repositories.
200      */
201     public static class AutoConfiguredMapperScannerRegistrar
202         implements BeanFactoryAware, ImportBeanDefinitionRegistrar, ResourceLoaderAware {
203 
204         private BeanFactory beanFactory;
205 
206         private ResourceLoader resourceLoader;
207 
208         @Override
209         public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
210 
211             logger.debug("Searching for mappers annotated with @Mapper");
212 
213             ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
214 
215             try {
216                 if (this.resourceLoader != null) {
217                     scanner.setResourceLoader(this.resourceLoader);
218                 }
219 
220                 List<String> packages = AutoConfigurationPackages.get(this.beanFactory);
221                 if (logger.isDebugEnabled()) {
222                     for (String pkg : packages) {
223                         logger.debug("Using auto-configuration base package '" + pkg + "'");
224                     }
225                 }
226 
227                 scanner.setAnnotationClass(Mapper.class);
228                 scanner.registerFilters();
229                 scanner.doScan(StringUtils.toStringArray(packages));
230             } catch (IllegalStateException ex) {
231                 logger.debug("Could not determine auto-configuration package, automatic mapper scanning disabled." + ex);
232             }
233         }
234 
235         @Override
236         public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
237             this.beanFactory = beanFactory;
238         }
239 
240         @Override
241         public void setResourceLoader(ResourceLoader resourceLoader) {
242             this.resourceLoader = resourceLoader;
243         }
244     }
245 
246     /**
247      * {@link org.mybatis.spring.annotation.MapperScan} ultimately ends up
248      * creating instances of {@link MapperFactoryBean}. If
249      * {@link org.mybatis.spring.annotation.MapperScan} is used then this
250      * auto-configuration is not needed. If it is _not_ used, however, then this
251      * will bring in a bean registrar and automatically register components based
252      * on the same component-scanning path as Spring Boot itself.
253      */
254     @org.springframework.context.annotation.Configuration
255     @Import({AutoConfiguredMapperScannerRegistrar.class})
256     @ConditionalOnMissingBean(MapperFactoryBean.class)
257     public static class MapperScannerRegistrarNotFoundConfiguration {
258 
259         @PostConstruct
260         public void afterPropertiesSet() {
261             logger.debug("No " + MapperFactoryBean.class.getName() + " found.");
262         }
263     }
264 
265 }
View Code

类似于Mybatis,注入了一些重要的配置。其中包括:

(1) SqlSessionTamplate : 之前在研究Mybatis 源码的时候研究过,是一个SeqSession,内部包含一个SqlSession 代理对象,代理对象采用JDK动态代理,代理对象内部每次都是调用SqlSessionUtils.getSqlSession 获取一个实际工作的三种会话工厂中的一个SqlSession 对象。

(2) SqlSessionFactory 会话工厂。和Mybatis一样,需要一个会话工厂,用于每次获取SqlSession 对象。这个SqlSessionFactory 是通过mybatisplus的MybatisSqlSessionFactoryBean 工厂Bean提供的。 其中配置了MybatisConfiguration (继承自Mybatis的Configuration 对象)、GlobalConfiguration 信息,重要的包括主键生成器、sql注入器、填充器。

com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean 源码如下:

/**
 * 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.spring;

import static org.springframework.util.Assert.notNull;
import static org.springframework.util.Assert.state;
import static org.springframework.util.ObjectUtils.isEmpty;
import static org.springframework.util.StringUtils.hasLength;
import static org.springframework.util.StringUtils.tokenizeToStringArray;

import java.io.IOException;
import java.sql.SQLException;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;

import javax.sql.DataSource;

import org.apache.ibatis.builder.xml.XMLMapperBuilder;
import org.apache.ibatis.cache.Cache;
import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.io.VFS;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import org.apache.ibatis.mapping.DatabaseIdProvider;
import org.apache.ibatis.mapping.Environment;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.reflection.factory.ObjectFactory;
import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.apache.ibatis.transaction.TransactionFactory;
import org.apache.ibatis.type.TypeHandler;
import org.apache.ibatis.type.TypeHandlerRegistry;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.transaction.SpringManagedTransactionFactory;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.core.NestedIOException;
import org.springframework.core.io.Resource;
import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;

import com.baomidou.mybatisplus.MybatisConfiguration;
import com.baomidou.mybatisplus.MybatisXMLConfigBuilder;
import com.baomidou.mybatisplus.entity.GlobalConfiguration;
import com.baomidou.mybatisplus.enums.IEnum;
import com.baomidou.mybatisplus.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.mapper.SqlRunner;
import com.baomidou.mybatisplus.toolkit.GlobalConfigUtils;
import com.baomidou.mybatisplus.toolkit.PackageHelper;

/**
 * <p>
 * 拷贝类 org.mybatis.spring.SqlSessionFactoryBean 修改方法 buildSqlSessionFactory()
 * 加载自定义 MybatisXmlConfigBuilder
 * </p>
 *
 * @author hubin
 * @Date 2017-01-04
 */
public class MybatisSqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {

    private static final Log LOGGER = LogFactory.getLog(SqlSessionFactoryBean.class);

    private Resource configLocation;

    private Configuration configuration;

    private Resource[] mapperLocations;

    private DataSource dataSource;

    private TransactionFactory transactionFactory;

    private Properties configurationProperties;

    private SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();

    private SqlSessionFactory sqlSessionFactory;

    //EnvironmentAware requires spring 3.1
    private String environment = MybatisSqlSessionFactoryBean.class.getSimpleName();

    private boolean failFast;

    private Interceptor[] plugins;

    private TypeHandler<?>[] typeHandlers;

    private String typeHandlersPackage;

    private Class<?>[] typeAliases;

    private String typeAliasesPackage;

    // TODO 自定义枚举包
    private String typeEnumsPackage;

    private Class<?> typeAliasesSuperType;

    //issue #19. No default provider.
    private DatabaseIdProvider databaseIdProvider;

    private Class<? extends VFS> vfs;

    private Cache cache;

    private ObjectFactory objectFactory;

    private ObjectWrapperFactory objectWrapperFactory;

    private GlobalConfiguration globalConfig = GlobalConfigUtils.defaults();

    // TODO 注入全局配置
    public void setGlobalConfig(GlobalConfiguration globalConfig) {
        this.globalConfig = globalConfig;
    }

    /**
     * Sets the ObjectFactory.
     *
     * @param objectFactory
     * @since 1.1.2
     */
    public void setObjectFactory(ObjectFactory objectFactory) {
        this.objectFactory = objectFactory;
    }

    /**
     * Sets the ObjectWrapperFactory.
     *
     * @param objectWrapperFactory
     * @since 1.1.2
     */
    public void setObjectWrapperFactory(ObjectWrapperFactory objectWrapperFactory) {
        this.objectWrapperFactory = objectWrapperFactory;
    }

    /**
     * Gets the DatabaseIdProvider
     *
     * @return
     * @since 1.1.0
     */
    public DatabaseIdProvider getDatabaseIdProvider() {
        return databaseIdProvider;
    }

    /**
     * Sets the DatabaseIdProvider.
     * As of version 1.2.2 this variable is not initialized by default.
     *
     * @param databaseIdProvider
     * @since 1.1.0
     */
    public void setDatabaseIdProvider(DatabaseIdProvider databaseIdProvider) {
        this.databaseIdProvider = databaseIdProvider;
    }

    public Class<? extends VFS> getVfs() {
        return this.vfs;
    }

    public void setVfs(Class<? extends VFS> vfs) {
        this.vfs = vfs;
    }

    public Cache getCache() {
        return this.cache;
    }

    public void setCache(Cache cache) {
        this.cache = cache;
    }

    /**
     * Mybatis plugin list.
     *
     * @param plugins list of plugins
     * @since 1.0.1
     */
    public void setPlugins(Interceptor[] plugins) {
        this.plugins = plugins;
    }

    /**
     * Packages to search for type aliases.
     *
     * @param typeAliasesPackage package to scan for domain objects
     * @since 1.0.1
     */
    public void setTypeAliasesPackage(String typeAliasesPackage) {
        this.typeAliasesPackage = typeAliasesPackage;
    }

    public void setTypeEnumsPackage(String typeEnumsPackage) {
        this.typeEnumsPackage = typeEnumsPackage;
    }

    /**
     * Super class which domain objects have to extend to have a type alias created.
     * No effect if there is no package to scan configured.
     *
     * @param typeAliasesSuperType super class for domain objects
     * @since 1.1.2
     */
    public void setTypeAliasesSuperType(Class<?> typeAliasesSuperType) {
        this.typeAliasesSuperType = typeAliasesSuperType;
    }

    /**
     * Packages to search for type handlers.
     *
     * @param typeHandlersPackage package to scan for type handlers
     * @since 1.0.1
     */
    public void setTypeHandlersPackage(String typeHandlersPackage) {
        this.typeHandlersPackage = typeHandlersPackage;
    }

    /**
     * Set type handlers. They must be annotated with {@code MappedTypes} and optionally with {@code MappedJdbcTypes}
     *
     * @param typeHandlers Type handler list
     * @since 1.0.1
     */
    public void setTypeHandlers(TypeHandler<?>[] typeHandlers) {
        this.typeHandlers = typeHandlers;
    }

    /**
     * List of type aliases to register. They can be annotated with {@code Alias}
     *
     * @param typeAliases Type aliases list
     * @since 1.0.1
     */
    public void setTypeAliases(Class<?>[] typeAliases) {
        this.typeAliases = typeAliases;
    }

    /**
     * If true, a final check is done on Configuration to assure that all mapped
     * statements are fully loaded and there is no one still pending to resolve
     * includes. Defaults to false.
     *
     * @param failFast enable failFast
     * @since 1.0.1
     */
    public void setFailFast(boolean failFast) {
        this.failFast = failFast;
    }

    /**
     * Set the location of the MyBatis {@code SqlSessionFactory} config file. A typical value is
     * "WEB-INF/mybatis-configuration.xml".
     */
    public void setConfigLocation(Resource configLocation) {
        this.configLocation = configLocation;
    }

    /**
     * Set a customized MyBatis configuration.
     *
     * @param configuration MyBatis configuration
     * @since 1.3.0
     */
    public void setConfiguration(Configuration configuration) {
        this.configuration = configuration;
    }

    /**
     * Set locations of MyBatis mapper files that are going to be merged into the {@code SqlSessionFactory}
     * configuration at runtime.
     * <p>
     * This is an alternative to specifying "&lt;sqlmapper&gt;" entries in an MyBatis config file.
     * This property being based on Spring's resource abstraction also allows for specifying
     * resource patterns here: e.g. "classpath*:sqlmap/*-mapper.xml".
     */
    public void setMapperLocations(Resource[] mapperLocations) {
        this.mapperLocations = mapperLocations;
    }

    /**
     * Set optional properties to be passed into the SqlSession configuration, as alternative to a
     * {@code &lt;properties&gt;} tag in the configuration xml file. This will be used to
     * resolve placeholders in the config file.
     */
    public void setConfigurationProperties(Properties sqlSessionFactoryProperties) {
        this.configurationProperties = sqlSessionFactoryProperties;
    }

    /**
     * Set the JDBC {@code DataSource} that this instance should manage transactions for. The {@code DataSource}
     * should match the one used by the {@code SqlSessionFactory}: for example, you could specify the same
     * JNDI DataSource for both.
     * <p>
     * A transactional JDBC {@code Connection} for this {@code DataSource} will be provided to application code
     * accessing this {@code DataSource} directly via {@code DataSourceUtils} or {@code DataSourceTransactionManager}.
     * <p>
     * The {@code DataSource} specified here should be the target {@code DataSource} to manage transactions for, not
     * a {@code TransactionAwareDataSourceProxy}. Only data access code may work with
     * {@code TransactionAwareDataSourceProxy}, while the transaction manager needs to work on the
     * underlying target {@code DataSource}. If there's nevertheless a {@code TransactionAwareDataSourceProxy}
     * passed in, it will be unwrapped to extract its target {@code DataSource}.
     */
    public void setDataSource(DataSource dataSource) {
        if (dataSource instanceof TransactionAwareDataSourceProxy) {
            // If we got a TransactionAwareDataSourceProxy, we need to perform
            // transactions for its underlying target DataSource, else data
            // access code won't see properly exposed transactions (i.e.
            // transactions for the target DataSource).
            this.dataSource = ((TransactionAwareDataSourceProxy) dataSource).getTargetDataSource();
        } else {
            this.dataSource = dataSource;
        }
    }

    /**
     * Sets the {@code SqlSessionFactoryBuilder} to use when creating the {@code SqlSessionFactory}.
     * <p>
     * This is mainly meant for testing so that mock SqlSessionFactory classes can be injected. By
     * default, {@code SqlSessionFactoryBuilder} creates {@code DefaultSqlSessionFactory} instances.
     */
    public void setSqlSessionFactoryBuilder(SqlSessionFactoryBuilder sqlSessionFactoryBuilder) {
        this.sqlSessionFactoryBuilder = sqlSessionFactoryBuilder;
    }

    /**
     * Set the MyBatis TransactionFactory to use. Default is {@code SpringManagedTransactionFactory}
     * <p>
     * The default {@code SpringManagedTransactionFactory} should be appropriate for all cases:
     * be it Spring transaction management, EJB CMT or plain JTA. If there is no active transaction,
     * SqlSession operations will execute SQL statements non-transactionally.
     * <p>
     * <b>It is strongly recommended to use the default {@code TransactionFactory}.</b> If not used, any
     * attempt at getting an SqlSession through Spring's MyBatis framework will throw an exception if
     * a transaction is active.
     *
     * @param transactionFactory the MyBatis TransactionFactory
     * @see SpringManagedTransactionFactory
     */
    public void setTransactionFactory(TransactionFactory transactionFactory) {
        this.transactionFactory = transactionFactory;
    }

    /**
     * <b>NOTE:</b> This class <em>overrides</em> any {@code Environment} you have set in the MyBatis
     * config file. This is used only as a placeholder name. The default value is
     * {@code SqlSessionFactoryBean.class.getSimpleName()}.
     *
     * @param environment the environment name
     */
    public void setEnvironment(String environment) {
        this.environment = environment;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        notNull(dataSource, "Property 'dataSource' is required");
        notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
        state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null),
            "Property 'configuration' and 'configLocation' can not specified with together");

        this.sqlSessionFactory = buildSqlSessionFactory();
    }

    /**
     * Build a {@code SqlSessionFactory} instance.
     * <p>
     * The default implementation uses the standard MyBatis {@code XMLConfigBuilder} API to build a
     * {@code SqlSessionFactory} instance based on an Reader.
     * Since 1.3.0, it can be specified a {@link Configuration} instance directly(without config file).
     *
     * @return SqlSessionFactory
     * @throws IOException if loading the config file failed
     */
    protected SqlSessionFactory buildSqlSessionFactory() throws Exception {

        Configuration configuration;

        // TODO 加载自定义 MybatisXmlConfigBuilder
        MybatisXMLConfigBuilder xmlConfigBuilder = null;
        if (this.configuration != null) {
            configuration = this.configuration;
            if (configuration.getVariables() == null) {
                configuration.setVariables(this.configurationProperties);
            } else if (this.configurationProperties != null) {
                configuration.getVariables().putAll(this.configurationProperties);
            }
        } else if (this.configLocation != null) {
            xmlConfigBuilder = new MybatisXMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
            configuration = xmlConfigBuilder.getConfiguration();
        } else {
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration");
            }
            // TODO 使用自定义配置
            configuration = new MybatisConfiguration();
            if (this.configurationProperties != null) {
                configuration.setVariables(this.configurationProperties);
            }
        }

        if (this.objectFactory != null) {
            configuration.setObjectFactory(this.objectFactory);
        }

        if (this.objectWrapperFactory != null) {
            configuration.setObjectWrapperFactory(this.objectWrapperFactory);
        }

        if (this.vfs != null) {
            configuration.setVfsImpl(this.vfs);
        }

        if (hasLength(this.typeAliasesPackage)) {
            // TODO 支持自定义通配符
            String[] typeAliasPackageArray;
            if (typeAliasesPackage.contains("*") && !typeAliasesPackage.contains(",")
                && !typeAliasesPackage.contains(";")) {
                typeAliasPackageArray = PackageHelper.convertTypeAliasesPackage(typeAliasesPackage);
            } else {
                typeAliasPackageArray = tokenizeToStringArray(this.typeAliasesPackage,
                    ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
            }
            if (typeAliasPackageArray == null) {
                throw new MybatisPlusException("not find typeAliasesPackage:" + typeAliasesPackage);
            }
            for (String packageToScan : typeAliasPackageArray) {
                configuration.getTypeAliasRegistry().registerAliases(packageToScan,
                    typeAliasesSuperType == null ? Object.class : typeAliasesSuperType);
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("Scanned package: '" + packageToScan + "' for aliases");
                }
            }
        }

        // TODO 自定义枚举类扫描处理
        if (hasLength(this.typeEnumsPackage)) {
            Set<Class> classes = null;
            if (typeEnumsPackage.contains("*") && !typeEnumsPackage.contains(",")
                && !typeEnumsPackage.contains(";")) {
                classes = PackageHelper.scanTypePackage(typeEnumsPackage);
            } else {
                String[] typeEnumsPackageArray = tokenizeToStringArray(this.typeEnumsPackage,
                    ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
                if (typeEnumsPackageArray == null) {
                    throw new MybatisPlusException("not find typeEnumsPackage:" + typeEnumsPackage);
                }
                classes = new HashSet<Class>();
                for (String typePackage : typeEnumsPackageArray) {
                    classes.addAll(PackageHelper.scanTypePackage(typePackage));
                }
            }
            // 取得类型转换注册器
            TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
            for (Class cls : classes) {
                if (cls.isEnum()) {
                    if (IEnum.class.isAssignableFrom(cls)) {
                        typeHandlerRegistry.register(cls.getName(), com.baomidou.mybatisplus.handlers.EnumTypeHandler.class.getCanonicalName());
                    } else {
                        // 使用原生 EnumOrdinalTypeHandler
                        typeHandlerRegistry.register(cls.getName(), org.apache.ibatis.type.EnumOrdinalTypeHandler.class.getCanonicalName());
                    }
                }
            }
        }

        if (!isEmpty(this.typeAliases)) {
            for (Class<?> typeAlias : this.typeAliases) {
                configuration.getTypeAliasRegistry().registerAlias(typeAlias);
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("Registered type alias: '" + typeAlias + "'");
                }
            }
        }

        if (!isEmpty(this.plugins)) {
            for (Interceptor plugin : this.plugins) {
                configuration.addInterceptor(plugin);
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("Registered plugin: '" + plugin + "'");
                }
            }
        }

        if (hasLength(this.typeHandlersPackage)) {
            String[] typeHandlersPackageArray = tokenizeToStringArray(this.typeHandlersPackage,
                ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
            for (String packageToScan : typeHandlersPackageArray) {
                configuration.getTypeHandlerRegistry().register(packageToScan);
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("Scanned package: '" + packageToScan + "' for type handlers");
                }
            }
        }

        if (!isEmpty(this.typeHandlers)) {
            for (TypeHandler<?> typeHandler : this.typeHandlers) {
                configuration.getTypeHandlerRegistry().register(typeHandler);
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("Registered type handler: '" + typeHandler + "'");
                }
            }
        }

        if (this.databaseIdProvider != null) {//fix #64 set databaseId before parse mapper xmls
            try {
                configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
            } catch (SQLException e) {
                throw new NestedIOException("Failed getting a databaseId", e);
            }
        }

        if (this.cache != null) {
            configuration.addCache(this.cache);
        }

        if (xmlConfigBuilder != null) {
            try {
                xmlConfigBuilder.parse();

                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("Parsed configuration file: '" + this.configLocation + "'");
                }
            } catch (Exception ex) {
                throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex);
            } finally {
                ErrorContext.instance().reset();
            }
        }

        if (this.transactionFactory == null) {
            this.transactionFactory = new SpringManagedTransactionFactory();
        }

        configuration.setEnvironment(new Environment(this.environment, this.transactionFactory, this.dataSource));
        // 设置元数据相关
        GlobalConfigUtils.setMetaData(dataSource, globalConfig);
        SqlSessionFactory sqlSessionFactory = this.sqlSessionFactoryBuilder.build(configuration);
        // TODO SqlRunner
        SqlRunner.FACTORY = sqlSessionFactory;
        // TODO 缓存 sqlSessionFactory
        globalConfig.setSqlSessionFactory(sqlSessionFactory);
        // TODO 设置全局参数属性
        globalConfig.signGlobalConfig(sqlSessionFactory);
        if (!isEmpty(this.mapperLocations)) {
            if (globalConfig.isRefresh()) {
                //TODO 设置自动刷新配置 减少配置
                new MybatisMapperRefresh(this.mapperLocations, sqlSessionFactory, 2,
                    2, true);
            }
            for (Resource mapperLocation : this.mapperLocations) {
                if (mapperLocation == null) {
                    continue;
                }

                try {
                    // TODO  这里也换了噢噢噢噢
                    XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
                        configuration, mapperLocation.toString(), configuration.getSqlFragments());
                    xmlMapperBuilder.parse();
                } catch (Exception e) {
                    throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
                } finally {
                    ErrorContext.instance().reset();
                }

                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("Parsed mapper file: '" + mapperLocation + "'");
                }
            }
        } else {
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Property 'mapperLocations' was not specified or no matching resources found");
            }
        }
        return sqlSessionFactory;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public SqlSessionFactory getObject() throws Exception {
        if (this.sqlSessionFactory == null) {
            afterPropertiesSet();
        }

        return this.sqlSessionFactory;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Class<? extends SqlSessionFactory> getObjectType() {
        return this.sqlSessionFactory == null ? SqlSessionFactory.class : this.sqlSessionFactory.getClass();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean isSingleton() {
        return true;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        if (failFast && event instanceof ContextRefreshedEvent) {
            // fail-fast -> check all statements are completed
            this.sqlSessionFactory.getConfiguration().getMappedStatementNames();
        }
    }
}
View Code

com.baomidou.mybatisplus.MybatisConfiguration 源码如下:

package com.baomidou.mybatisplus;

import org.apache.ibatis.binding.MapperRegistry;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSession;

import com.baomidou.mybatisplus.toolkit.GlobalConfigUtils;

/**
 * <p>
 * replace default Configuration class
 * </p>
 * <p>
 * Caratacus 2016/9/25 replace mapperRegistry
 * </p>
 *
 * @author hubin
 * @Date 2016-01-23
 */
public class MybatisConfiguration extends Configuration {

    private static final Log logger = LogFactory.getLog(MybatisConfiguration.class);

    /**
     * Mapper 注册
     */
    public final MybatisMapperRegistry mybatisMapperRegistry = new MybatisMapperRegistry(this);

    /**
     * 初始化调用
     */
    public MybatisConfiguration() {
        this.setDefaultScriptingLanguage(MybatisXMLLanguageDriver.class);
        logger.debug("Mybatis-plus init success.");
    }

    /**
     * <p>
     * MybatisPlus 加载 SQL 顺序:
     * </p>
     * 1、加载XML中的SQL<br>
     * 2、加载sqlProvider中的SQL<br>
     * 3、xmlSql 与 sqlProvider不能包含相同的SQL<br>
     * <br>
     * 调整后的SQL优先级:xmlSql > sqlProvider > curdSql <br>
     */
    @Override
    public void addMappedStatement(MappedStatement ms) {
        logger.debug("addMappedStatement: " + ms.getId());
        if (GlobalConfigUtils.isRefresh(ms.getConfiguration())) {
            /*
             * 支持是否自动刷新 XML 变更内容,开发环境使用【 注:生产环境勿用!】
             */
            this.mappedStatements.remove(ms.getId());
        } else {
            if (this.mappedStatements.containsKey(ms.getId())) {
                /*
                 * 说明已加载了xml中的节点; 忽略mapper中的SqlProvider数据
                 */
                logger.error("mapper[" + ms.getId() + "] is ignored, because it's exists, maybe from xml file");
                return;
            }
        }
        super.addMappedStatement(ms);
    }

    @Override
    public void setDefaultScriptingLanguage(Class<?> driver) {
        if (driver == null) {
            /* 设置自定义 driver */
            driver = MybatisXMLLanguageDriver.class;
        }
        super.setDefaultScriptingLanguage(driver);
    }

    @Override
    public MapperRegistry getMapperRegistry() {
        return mybatisMapperRegistry;
    }

    @Override
    public <T> void addMapper(Class<T> type) {
        mybatisMapperRegistry.addMapper(type);
    }

    @Override
    public void addMappers(String packageName, Class<?> superType) {
        mybatisMapperRegistry.addMappers(packageName, superType);
    }

    @Override
    public void addMappers(String packageName) {
        mybatisMapperRegistry.addMappers(packageName);
    }

    @Override
    public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
        return mybatisMapperRegistry.getMapper(type, sqlSession);
    }

    @Override
    public boolean hasMapper(Class<?> type) {
        return mybatisMapperRegistry.hasMapper(type);
    }

}
View Code

com.baomidou.mybatisplus.mapper.AutoSqlInjector 源码如下:

/**
 * 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.mapper;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.ibatis.builder.MapperBuilderAssistant;
import org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator;
import org.apache.ibatis.executor.keygen.KeyGenerator;
import org.apache.ibatis.executor.keygen.NoKeyGenerator;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ResultMap;
import org.apache.ibatis.mapping.ResultMapping;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.mapping.StatementType;
import org.apache.ibatis.scripting.LanguageDriver;
import org.apache.ibatis.scripting.defaults.RawSqlSource;
import org.apache.ibatis.session.Configuration;

import com.baomidou.mybatisplus.entity.GlobalConfiguration;
import com.baomidou.mybatisplus.entity.TableFieldInfo;
import com.baomidou.mybatisplus.entity.TableInfo;
import com.baomidou.mybatisplus.enums.FieldFill;
import com.baomidou.mybatisplus.enums.FieldStrategy;
import com.baomidou.mybatisplus.enums.IdType;
import com.baomidou.mybatisplus.enums.SqlMethod;
import com.baomidou.mybatisplus.toolkit.ArrayUtils;
import com.baomidou.mybatisplus.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.toolkit.GlobalConfigUtils;
import com.baomidou.mybatisplus.toolkit.PluginUtils;
import com.baomidou.mybatisplus.toolkit.SqlReservedWords;
import com.baomidou.mybatisplus.toolkit.StringUtils;
import com.baomidou.mybatisplus.toolkit.TableInfoHelper;

/**
 * <p>
 * SQL 自动注入器
 * </p>
 *
 * @author hubin sjy tantan
 * @Date 2016-09-09
 */
public class AutoSqlInjector implements ISqlInjector {

    private static final Log logger = LogFactory.getLog(AutoSqlInjector.class);

    protected Configuration configuration;
    protected LanguageDriver languageDriver;
    protected MapperBuilderAssistant builderAssistant;

    /**
     * <p>
     * CRUD 注入后给予标识 注入过后不再注入
     * </p>
     *
     * @param builderAssistant
     * @param mapperClass
     */
    @Override
    public void inspectInject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass) {
        String className = mapperClass.toString();
        Set<String> mapperRegistryCache = GlobalConfigUtils.getMapperRegistryCache(builderAssistant.getConfiguration());
        if (!mapperRegistryCache.contains(className)) {
            inject(builderAssistant, mapperClass);
            mapperRegistryCache.add(className);
        }
    }

    /**
     * 注入单点 crudSql
     */
    @Override
    public void inject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass) {
        this.configuration = builderAssistant.getConfiguration();
        this.builderAssistant = builderAssistant;
        this.languageDriver = configuration.getDefaultScriptingLanguageInstance();

        //去除 驼峰设置 PLUS 配置 > 原生配置 (该配置不需要与原生Mybatis混淆)
        /*if (!globalCache.isDbColumnUnderline()) {
            globalCache.setDbColumnUnderline(configuration.isMapUnderscoreToCamelCase());
        }*/
        Class<?> modelClass = extractModelClass(mapperClass);
        if (null != modelClass) {
            /**
             * 初始化 SQL 解析
             */
            if (this.getGlobalConfig().isSqlParserCache()) {
                PluginUtils.initSqlParserInfoCache(mapperClass);
            }
            TableInfo table = TableInfoHelper.initTableInfo(builderAssistant, modelClass);
            injectSql(builderAssistant, mapperClass, modelClass, table);
        }
    }

    /**
     * <p>
     * 注入SQL
     * </p>
     *
     * @param builderAssistant
     * @param mapperClass
     * @param modelClass
     * @param table
     */
    protected void injectSql(MapperBuilderAssistant builderAssistant, Class<?> mapperClass, Class<?> modelClass, TableInfo table) {
        /**
         * #148 表信息包含主键,注入主键相关方法
         */
        if (StringUtils.isNotEmpty(table.getKeyProperty())) {
            /** 删除 */
            this.injectDeleteByIdSql(false, mapperClass, modelClass, table);
            this.injectDeleteByIdSql(true, mapperClass, modelClass, table);
            /** 修改 */
            this.injectUpdateByIdSql(true, mapperClass, modelClass, table);
            this.injectUpdateByIdSql(false, mapperClass, modelClass, table);
            /** 查询 */
            this.injectSelectByIdSql(false, mapperClass, modelClass, table);
            this.injectSelectByIdSql(true, mapperClass, modelClass, table);
        } else {
            // 表不包含主键时 给予警告
            logger.warn(String.format("%s ,Not found @TableId annotation, Cannot use Mybatis-Plus 'xxById' Method.",
                modelClass.toString()));
        }
        /**
         * 正常注入无需主键方法
         */
        /** 插入 */
        this.injectInsertOneSql(true, mapperClass, modelClass, table);
        this.injectInsertOneSql(false, mapperClass, modelClass, table);
        /** 删除 */
        this.injectDeleteSql(mapperClass, modelClass, table);
        this.injectDeleteByMapSql(mapperClass, table);
        /** 修改 */
        this.injectUpdateSql(mapperClass, modelClass, table);
        /** 修改 (自定义 set 属性) */
        this.injectUpdateForSetSql(mapperClass, modelClass, table);
        /** 查询 */
        this.injectSelectByMapSql(mapperClass, modelClass, table);
        this.injectSelectOneSql(mapperClass, modelClass, table);
        this.injectSelectCountSql(mapperClass, modelClass, table);
        this.injectSelectListSql(SqlMethod.SELECT_LIST, mapperClass, modelClass, table);
        this.injectSelectListSql(SqlMethod.SELECT_PAGE, mapperClass, modelClass, table);
        this.injectSelectMapsSql(SqlMethod.SELECT_MAPS, mapperClass, modelClass, table);
        this.injectSelectMapsSql(SqlMethod.SELECT_MAPS_PAGE, mapperClass, modelClass, table);
        this.injectSelectObjsSql(SqlMethod.SELECT_OBJS, mapperClass, modelClass, table);
        /** 自定义方法 */
        this.inject(configuration, builderAssistant, mapperClass, modelClass, table);
    }

    /**
     * 自定义方法,注入点(子类需重写该方法)
     */
    public void inject(Configuration configuration, MapperBuilderAssistant builderAssistant, Class<?> mapperClass,
                       Class<?> modelClass, TableInfo table) {
        // to do nothing
    }

    /**
     * 提取泛型模型,多泛型的时候请将泛型T放在第一位
     *
     * @param mapperClass
     * @return
     */
    protected Class<?> extractModelClass(Class<?> mapperClass) {
        Type[] types = mapperClass.getGenericInterfaces();
        ParameterizedType target = null;
        for (Type type : types) {
            if (type instanceof ParameterizedType) {
                Type[] typeArray = ((ParameterizedType) type).getActualTypeArguments();
                if (ArrayUtils.isNotEmpty(typeArray)) {
                    for (Type t : typeArray) {
                        if (t instanceof TypeVariable || t instanceof WildcardType) {
                            target = null;
                            break;
                        } else {
                            target = (ParameterizedType) type;
                            break;
                        }
                    }
                }
                break;
            }
        }
        return target == null ? null : (Class<?>) target.getActualTypeArguments()[0];
    }

    /**
     * <p>
     * 注入插入 SQL 语句
     * </p>
     *
     * @param selective   是否选择插入
     * @param mapperClass
     * @param modelClass
     * @param table
     */
    protected void injectInsertOneSql(boolean selective, Class<?> mapperClass, Class<?> modelClass, TableInfo table) {
        /*
         * INSERT INTO table <trim prefix="(" suffix=")" suffixOverrides=",">
         * <if test="xx != null">xx,</if> </trim> <trim prefix="values ("
         * suffix=")" suffixOverrides=","> <if test="xx != null">#{xx},</if>
         * </trim>
         */
        KeyGenerator keyGenerator = new NoKeyGenerator();
        StringBuilder fieldBuilder = new StringBuilder();
        StringBuilder placeholderBuilder = new StringBuilder();
        SqlMethod sqlMethod = selective ? SqlMethod.INSERT_ONE : SqlMethod.INSERT_ONE_ALL_COLUMN;

        fieldBuilder.append("
<trim prefix="(" suffix=")" suffixOverrides=",">
");
        placeholderBuilder.append("
<trim prefix="(" suffix=")" suffixOverrides=",">
");
        String keyProperty = null;
        String keyColumn = null;

        // 表包含主键处理逻辑,如果不包含主键当普通字段处理
        if (StringUtils.isNotEmpty(table.getKeyProperty())) {
            if (table.getIdType() == IdType.AUTO) {
                /** 自增主键 */
                keyGenerator = new Jdbc3KeyGenerator();
                keyProperty = table.getKeyProperty();
                keyColumn = table.getKeyColumn();
            } else {
                if (null != table.getKeySequence()) {
                    keyGenerator = TableInfoHelper.genKeyGenerator(table, builderAssistant, sqlMethod.getMethod(), languageDriver);
                    keyProperty = table.getKeyProperty();
                    keyColumn = table.getKeyColumn();
                    fieldBuilder.append(table.getKeyColumn()).append(",");
                    placeholderBuilder.append("#{").append(table.getKeyProperty()).append("},");
                } else {
                    /** 用户输入自定义ID */
                    fieldBuilder.append(table.getKeyColumn()).append(",");
                    // 正常自定义主键策略
                    placeholderBuilder.append("#{").append(table.getKeyProperty()).append("},");
                }
            }
        }

        // 是否 IF 标签判断
        boolean ifTag;
        List<TableFieldInfo> fieldList = table.getFieldList();
        for (TableFieldInfo fieldInfo : fieldList) {
            // 在FieldIgnore,INSERT_UPDATE,INSERT 时设置为false
            ifTag = !(FieldFill.INSERT == fieldInfo.getFieldFill()
                || FieldFill.INSERT_UPDATE == fieldInfo.getFieldFill());
            if (selective && ifTag) {
                fieldBuilder.append(convertIfTagIgnored(fieldInfo, false));
                fieldBuilder.append(fieldInfo.getColumn()).append(",");
                fieldBuilder.append(convertIfTagIgnored(fieldInfo, true));
                placeholderBuilder.append(convertIfTagIgnored(fieldInfo, false));
                placeholderBuilder.append("#{").append(fieldInfo.getEl()).append("},");
                placeholderBuilder.append(convertIfTagIgnored(fieldInfo, true));
            } else {
                fieldBuilder.append(fieldInfo.getColumn()).append(",");
                placeholderBuilder.append("#{").append(fieldInfo.getEl()).append("},");
            }
        }
        fieldBuilder.append("
</trim>");
        placeholderBuilder.append("
</trim>");
        String sql = String.format(sqlMethod.getSql(), table.getTableName(), fieldBuilder.toString(),
            placeholderBuilder.toString());
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
        this.addInsertMappedStatement(mapperClass, modelClass, sqlMethod.getMethod(), sqlSource, keyGenerator, keyProperty,
            keyColumn);
    }

    /**
     * <p>
     * 注入 entity 条件删除 SQL 语句
     * </p>
     *
     * @param mapperClass
     * @param modelClass
     * @param table
     */
    protected void injectDeleteSql(Class<?> mapperClass, Class<?> modelClass, TableInfo table) {
        SqlMethod sqlMethod = SqlMethod.DELETE;
        String sql = String.format(sqlMethod.getSql(), table.getTableName(), sqlWhereEntityWrapper(table));
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
        this.addDeleteMappedStatement(mapperClass, sqlMethod.getMethod(), sqlSource);
    }

    /**
     * <p>
     * 注入 map 条件删除 SQL 语句
     * </p>
     *
     * @param mapperClass
     * @param table
     */
    protected void injectDeleteByMapSql(Class<?> mapperClass, TableInfo table) {
        SqlMethod sqlMethod = SqlMethod.DELETE_BY_MAP;
        String sql = String.format(sqlMethod.getSql(), table.getTableName(), sqlWhereByMap(table));
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, Map.class);
        this.addDeleteMappedStatement(mapperClass, sqlMethod.getMethod(), sqlSource);
    }

    /**
     * <p>
     * 注入删除 SQL 语句
     * </p>
     *
     * @param mapperClass
     * @param modelClass
     * @param table
     */
    protected void injectDeleteByIdSql(boolean batch, Class<?> mapperClass, Class<?> modelClass, TableInfo table) {
        SqlMethod sqlMethod = SqlMethod.DELETE_BY_ID;
        SqlSource sqlSource;
        // 因为后面要通过get方法获取类型,所以这里要获取key的属性值
        String idStr = table.getKeyProperty();
        if (batch) {
            sqlMethod = SqlMethod.DELETE_BATCH_BY_IDS;
            StringBuilder ids = new StringBuilder();
            ids.append("
<foreach item="item" index="index" collection="coll" separator=",">");
            ids.append("#{item}");
            ids.append("
</foreach>");
            idStr = ids.toString();
        }
        String sql = String.format(sqlMethod.getSql(), table.getTableName(), table.getKeyColumn(), idStr);
        sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
        this.addDeleteMappedStatement(mapperClass, sqlMethod.getMethod(), sqlSource);
    }

    /**
     * <p>
     * 注入更新 SQL 语句
     * </p>
     *
     * @param mapperClass
     * @param modelClass
     * @param table
     */
    protected void injectUpdateByIdSql(boolean selective, Class<?> mapperClass, Class<?> modelClass, TableInfo table) {
        SqlMethod sqlMethod = selective ? SqlMethod.UPDATE_BY_ID : SqlMethod.UPDATE_ALL_COLUMN_BY_ID;
        String sql = String.format(sqlMethod.getSql(), table.getTableName(), sqlSet(selective, table, "et."), table.getKeyColumn(),
            "et." + table.getKeyProperty(),
            "<if test="et instanceof java.util.Map">"
                + "<if test="et.MP_OPTLOCK_VERSION_ORIGINAL!=null">"
                + "and ${et.MP_OPTLOCK_VERSION_COLUMN}=#{et.MP_OPTLOCK_VERSION_ORIGINAL}"
                + "</if>"
                + "</if>"
        );
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
        this.addUpdateMappedStatement(mapperClass, modelClass, sqlMethod.getMethod(), sqlSource);
    }

    /**
     * <p>
     * 注入批量更新 SQL 语句
     * </p>
     *
     * @param mapperClass
     * @param modelClass
     * @param table
     */
    protected void injectUpdateSql(Class<?> mapperClass, Class<?> modelClass, TableInfo table) {
        SqlMethod sqlMethod = SqlMethod.UPDATE;
        String sql = String.format(sqlMethod.getSql(), table.getTableName(), sqlSet(true, table, "et."), sqlWhereEntityWrapper(table));
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
        this.addUpdateMappedStatement(mapperClass, modelClass, sqlMethod.getMethod(), sqlSource);
    }

    /**
     * <p>
     * 注入批量更新 SQL 语句
     * </p>
     *
     * @param mapperClass
     * @param modelClass
     * @param table
     */
    protected void injectUpdateForSetSql(Class<?> mapperClass, Class<?> modelClass, TableInfo table) {
        SqlMethod sqlMethod = SqlMethod.UPDATE_FOR_SET;
        String sql = String.format(sqlMethod.getSql(), table.getTableName(), customSqlSet(), sqlWhereEntityWrapper(table));
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
        this.addUpdateMappedStatement(mapperClass, modelClass, sqlMethod.getMethod(), sqlSource);
    }

    /**
     * <p>
     * 注入查询 SQL 语句
     * </p>
     *
     * @param batch       是否为批量插入
     * @param mapperClass
     * @param modelClass
     * @param table
     */
    protected void injectSelectByIdSql(boolean batch, Class<?> mapperClass, Class<?> modelClass, TableInfo table) {
        SqlMethod sqlMethod = SqlMethod.SELECT_BY_ID;
        SqlSource sqlSource;
        if (batch) {
            sqlMethod = SqlMethod.SELECT_BATCH_BY_IDS;
            StringBuilder ids = new StringBuilder();
            ids.append("
<foreach item="item" index="index" collection="coll" separator=",">");
            ids.append("#{item}");
            ids.append("
</foreach>");
            sqlSource = languageDriver.createSqlSource(configuration, String.format(sqlMethod.getSql(),
                sqlSelectColumns(table, false), table.getTableName(), table.getKeyColumn(), ids.toString()), modelClass);
        } else {
            sqlSource = new RawSqlSource(configuration, String.format(sqlMethod.getSql(), sqlSelectColumns(table, false),
                table.getTableName(), table.getKeyColumn(), table.getKeyProperty()), Object.class);
        }
        this.addSelectMappedStatement(mapperClass, sqlMethod.getMethod(), sqlSource, modelClass, table);
    }

    /**
     * <p>
     * 注入 map 查询 SQL 语句
     * </p>
     *
     * @param mapperClass
     * @param modelClass
     * @param table
     */
    protected void injectSelectByMapSql(Class<?> mapperClass, Class<?> modelClass, TableInfo table) {
        SqlMethod sqlMethod = SqlMethod.SELECT_BY_MAP;
        String sql = String.format(sqlMethod.getSql(), sqlSelectColumns(table, false), table.getTableName(), sqlWhereByMap(table));
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, Map.class);
        this.addSelectMappedStatement(mapperClass, sqlMethod.getMethod(), sqlSource, modelClass, table);
    }

    /**
     * <p>
     * 注入实体查询一条记录 SQL 语句
     * </p>
     *
     * @param mapperClass
     * @param modelClass
     * @param table
     */
    protected void injectSelectOneSql(Class<?> mapperClass, Class<?> modelClass, TableInfo table) {
        SqlMethod sqlMethod = SqlMethod.SELECT_ONE;
        String sql = String.format(sqlMethod.getSql(), sqlSelectColumns(table, false), table.getTableName(), sqlWhere(table));
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
        this.addSelectMappedStatement(mapperClass, sqlMethod.getMethod(), sqlSource, modelClass, table);
    }

    /**
     * <p>
     * 注入EntityWrapper方式查询记录列表 SQL 语句
     * </p>
     *
     * @param sqlMethod
     * @param mapperClass
     * @param modelClass
     * @param table
     */
    protected void injectSelectListSql(SqlMethod sqlMethod, Class<?> mapperClass, Class<?> modelClass, TableInfo table) {
        String sql = String.format(sqlMethod.getSql(), sqlSelectColumns(table, true), table.getTableName(),
            sqlWhereEntityWrapper(table));
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
        this.addSelectMappedStatement(mapperClass, sqlMethod.getMethod(), sqlSource, modelClass, table);
    }

    /**
     * <p>
     * 注入EntityWrapper方式查询记录列表 SQL 语句
     * </p>
     *
     * @param sqlMethod
     * @param mapperClass
     * @param modelClass
     * @param table
     */
    protected void injectSelectMapsSql(SqlMethod sqlMethod, Class<?> mapperClass, Class<?> modelClass, TableInfo table) {
        String sql = String.format(sqlMethod.getSql(), sqlSelectColumns(table, true), table.getTableName(),
            sqlWhereEntityWrapper(table));
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
        this.addSelectMappedStatement(mapperClass, sqlMethod.getMethod(), sqlSource, Map.class, table);
    }

    /**
     * <p>
     * 注入EntityWrapper方式查询记录列表 SQL 语句
     * </p>
     *
     * @param sqlMethod
     * @param mapperClass
     * @param modelClass
     * @param table
     */
    protected void injectSelectObjsSql(SqlMethod sqlMethod, Class<?> mapperClass, Class<?> modelClass, TableInfo table) {
        String sql = String.format(sqlMethod.getSql(), sqlSelectObjsColumns(table), table.getTableName(),
            sqlWhereEntityWrapper(table));
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
        this.addSelectMappedStatement(mapperClass, sqlMethod.getMethod(), sqlSource, Object.class, table);
    }

    /**
     * <p>
     * 注入EntityWrapper查询总记录数 SQL 语句
     * </p>
     *
     * @param mapperClass
     * @param modelClass
     * @param table       表信息
     */
    protected void injectSelectCountSql(Class<?> mapperClass, Class<?> modelClass, TableInfo table) {
        SqlMethod sqlMethod = SqlMethod.SELECT_COUNT;
        String sql = String.format(sqlMethod.getSql(), table.getTableName(), sqlWhereEntityWrapper(table));
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
        this.addSelectMappedStatement(mapperClass, sqlMethod.getMethod(), sqlSource, Integer.class, null);
    }

    /**
     * <p>
     * Sql 运算条件
     * </p>
     */
    protected String sqlCondition(String condition, String column, String property) {
        return String.format(condition, column, property);
    }

    /**
     * <p>
     * EntityWrapper方式获取select where
     * </p>
     *
     * @param table 表信息
     * @return String
     */
    protected String sqlWhereEntityWrapper(TableInfo table) {
        StringBuilder where = new StringBuilder(128);
        where.append("
<where>");
        where.append("
<if test="ew!=null">");
        where.append("
<if test="ew.entity!=null">");
        if (StringUtils.isNotEmpty(table.getKeyProperty())) {
            where.append("
<if test="ew.entity.").append(table.getKeyProperty()).append("!=null">
");
            where.append(table.getKeyColumn()).append("=#{ew.entity.").append(table.getKeyProperty()).append("}");
            where.append("
</if>");
        }
        List<TableFieldInfo> fieldList = table.getFieldList();
        for (TableFieldInfo fieldInfo : fieldList) {
            where.append(convertIfTag(fieldInfo, "ew.entity.", false));
            where.append(" AND ").append(this.sqlCondition(fieldInfo.getCondition(),
                fieldInfo.getColumn(), "ew.entity." + fieldInfo.getEl()));
            where.append(convertIfTag(fieldInfo, true));
        }
        where.append("
</if>");
        where.append("
<if test="ew!=null and ew.sqlSegment!=null and ew.notEmptyOfWhere">
${ew.sqlSegment}
</if>");
        where.append("
</if>");
        where.append("
</where>");
        where.append("
<if test="ew!=null and ew.sqlSegment!=null and ew.emptyOfWhere">
${ew.sqlSegment}
</if>");
        return where.toString();
    }

    /**
     * <p>
     * SQL 更新 set 语句
     * </p>
     *
     * @param selective 是否选择判断
     * @param table     表信息
     * @param prefix    前缀
     * @return
     */
    protected String sqlSet(boolean selective, TableInfo table, String prefix) {
        StringBuilder set = new StringBuilder();
        set.append("<trim prefix="SET" suffixOverrides=",">");

        // 是否 IF 标签判断
        boolean ifTag;
        List<TableFieldInfo> fieldList = table.getFieldList();
        for (TableFieldInfo fieldInfo : fieldList) {
            // 判断是否更新忽略,在FieldIgnore,UPDATE,INSERT_UPDATE设置为false
            ifTag = !(FieldFill.UPDATE == fieldInfo.getFieldFill()
                || FieldFill.INSERT_UPDATE == fieldInfo.getFieldFill());
            if (selective && ifTag) {
                if (StringUtils.isNotEmpty(fieldInfo.getUpdate())) {
                    set.append(fieldInfo.getColumn()).append("=");
                    set.append(String.format(fieldInfo.getUpdate(), fieldInfo.getColumn())).append(",");
                } else {
                    set.append(convertIfTag(true, fieldInfo, prefix, false));
                    set.append(fieldInfo.getColumn()).append("=#{");
                    if (null != prefix) {
                        set.append(prefix);
                    }
                    set.append(fieldInfo.getEl()).append("},");
                    set.append(convertIfTag(true, fieldInfo, null, true));
                }
            } else if (FieldFill.INSERT != fieldInfo.getFieldFill()) {
                // 排除填充注解字段
                set.append(fieldInfo.getColumn()).append("=#{");
                if (null != prefix) {
                    set.append(prefix);
                }
                set.append(fieldInfo.getEl()).append("},");
            }
        }
        set.append("
</trim>");
        return set.toString();
    }

    /**
     * <p>
     * SQL 自定义更新 set 语句
     * </p>
     *
     * @return
     */
    protected String customSqlSet() {
        StringBuilder set = new StringBuilder();
        set.append("<trim prefix="SET" suffixOverrides=",">");
        set.append("
${setStr}");
        set.append("
</trim>");
        return set.toString();
    }

    /**
     * <p>
     * 获取需要转义的SQL字段
     * </p>
     *
     * @param convertStr
     * @return
     */
    protected String sqlWordConvert(String convertStr) {
        GlobalConfiguration globalConfig = GlobalConfigUtils.getGlobalConfig(configuration);
        return SqlReservedWords.convert(globalConfig, convertStr);
    }

    /**
     * <p>
     * SQL 查询所有表字段
     * </p>
     *
     * @param table
     * @param entityWrapper 是否为包装类型查询
     * @return
     */
    protected String sqlSelectColumns(TableInfo table, boolean entityWrapper) {
        StringBuilder columns = new StringBuilder();
        if (null != table.getResultMap()) {
            /*
             * 存在 resultMap 映射返回
             */
            if (entityWrapper) {
                columns.append("<choose><when test="ew != null and ew.sqlSelect != null">${ew.sqlSelect}</when><otherwise>");
            }
            columns.append("*");
            if (entityWrapper) {
                columns.append("</otherwise></choose>");
            }
        } else {
            /*
             * 普通查询
             */
            if (entityWrapper) {
                columns.append("<choose><when test="ew != null and ew.sqlSelect != null">${ew.sqlSelect}</when><otherwise>");
            }
            List<TableFieldInfo> fieldList = table.getFieldList();
            int size = 0;
            if (null != fieldList) {
                size = fieldList.size();
            }

            // 主键处理
            if (StringUtils.isNotEmpty(table.getKeyProperty())) {
                if (table.isKeyRelated()) {
                    columns.append(table.getKeyColumn()).append(" AS ").append(sqlWordConvert(table.getKeyProperty()));
                } else {
                    columns.append(sqlWordConvert(table.getKeyProperty()));
                }
                if (size >= 1) {
                    // 判断其余字段是否存在
                    columns.append(",");
                }
            }

            if (size >= 1) {
                // 字段处理
                int i = 0;
                Iterator<TableFieldInfo> iterator = fieldList.iterator();
                while (iterator.hasNext()) {
                    TableFieldInfo fieldInfo = iterator.next();
                    // 匹配转换内容
                    String wordConvert = sqlWordConvert(fieldInfo.getProperty());
                    if (fieldInfo.getColumn().equals(wordConvert)) {
                        columns.append(wordConvert);
                    } else {
                        // 字段属性不一致
                        columns.append(fieldInfo.getColumn());
                        columns.append(" AS ").append(wordConvert);
                    }
                    if (i + 1 < size) {
                        columns.append(",");
                    }
                    i++;
                }
            }
            if (entityWrapper) {
                columns.append("</otherwise></choose>");
            }
        }

        /*
         * 返回所有查询字段内容
         */
        return columns.toString();
    }

    /**
     * <p>
     * SQL 设置selectObj sqlselect
     * </p>
     *
     * @param table 是否为包装类型查询
     * @return
     */
    protected String sqlSelectObjsColumns(TableInfo table) {
        StringBuilder columns = new StringBuilder();
        /*
         * 普通查询
         */
        columns.append("<choose><when test="ew != null and ew.sqlSelect != null">${ew.sqlSelect}</when><otherwise>");
        // 主键处理
        if (StringUtils.isNotEmpty(table.getKeyProperty())) {
            if (table.isKeyRelated()) {
                columns.append(table.getKeyColumn()).append(" AS ").append(sqlWordConvert(table.getKeyProperty()));
            } else {
                columns.append(sqlWordConvert(table.getKeyProperty()));
            }
        } else {
            // 表字段处理
            List<TableFieldInfo> fieldList = table.getFieldList();
            if (CollectionUtils.isNotEmpty(fieldList)) {
                TableFieldInfo fieldInfo = fieldList.get(0);
                // 匹配转换内容
                String wordConvert = sqlWordConvert(fieldInfo.getProperty());
                if (fieldInfo.getColumn().equals(wordConvert)) {
                    columns.append(wordConvert);
                } else {
                    // 字段属性不一致
                    columns.append(fieldInfo.getColumn());
                    columns.append(" AS ").append(wordConvert);
                }
            }
        }
        columns.append("</otherwise></choose>");
        return columns.toString();
    }

    /**
     * <p>
     * SQL 查询条件
     * </p>
     */
    protected String sqlWhere(TableInfo table) {
        StringBuilder where = new StringBuilder();
        where.append("
<where>");
        if (StringUtils.isNotEmpty(table.getKeyProperty())) {
            where.append("
<if test="ew.").append(table.getKeyProperty()).append("!=null">
");
            where.append(table.getKeyColumn()).append("=#{ew.").append(table.getKeyProperty()).append("}");
            where.append("
</if>");
        }
        List<TableFieldInfo> fieldList = table.getFieldList();
        for (TableFieldInfo fieldInfo : fieldList) {
            where.append(convertIfTag(fieldInfo, "ew.", false));
            where.append(" AND ").append(this.sqlCondition(fieldInfo.getCondition(),
                fieldInfo.getColumn(), "ew." + fieldInfo.getEl()));
            where.append(convertIfTag(fieldInfo, true));
        }
        where.append("
</where>");
        return where.toString();
    }

    /**
     * <p>
     * SQL map 查询条件
     * </p>
     */
    protected String sqlWhereByMap(TableInfo table) {
        StringBuilder where = new StringBuilder();
        where.append("
<if test="cm!=null and !cm.isEmpty">");
        where.append("
<where>");
        where.append("
<foreach collection="cm.keys" item="k" separator="AND">");
        where.append("
<if test="cm[k] != null">");
        where.append("
").append(SqlReservedWords.convert(getGlobalConfig(), "${k}")).append(" = #{cm[${k}]}");
        where.append("
</if>");
        where.append("
</foreach>");
        where.append("
</where>");
        where.append("
</if>");
        return where.toString();
    }

    /**
     * <p>
     * IF 条件转换方法
     * </p>
     *
     * @param ignored   允许忽略
     * @param fieldInfo 字段信息
     * @param prefix    条件前缀
     * @param close     是否闭合标签
     * @return
     */
    protected String convertIfTag(boolean ignored, TableFieldInfo fieldInfo, String prefix, boolean close) {
        /** 忽略策略 */
        FieldStrategy fieldStrategy = fieldInfo.getFieldStrategy();
        if (fieldStrategy == FieldStrategy.IGNORED) {
            if (ignored) {
                return "";
            }
            // 查询策略,使用全局策略
            fieldStrategy = this.getGlobalConfig().getFieldStrategy();
        }

        // 关闭标签
        if (close) {
            return "</if>";
        }

        /** 前缀处理 */
        String property = fieldInfo.getProperty();
        Class propertyType = fieldInfo.getPropertyType();
        property = StringUtils.removeIsPrefixIfBoolean(property, propertyType);
        if (null != prefix) {
            property = prefix + property;
        }
        // 验证逻辑
        if (fieldStrategy == FieldStrategy.NOT_EMPTY) {
            if (StringUtils.isCharSequence(propertyType)) {
                return String.format("
	<if test="%s!=null and %s!=''">", property, property);
            } else {
                return String.format("
	<if test="%s!=null ">", property);
            }
        } else {
            // FieldStrategy.NOT_NULL
            return String.format("
	<if test="%s!=null">", property);
        }
    }

    protected String convertIfTagIgnored(TableFieldInfo fieldInfo, boolean close) {
        return convertIfTag(true, fieldInfo, null, close);
    }

    protected String convertIfTag(TableFieldInfo fieldInfo, String prefix, boolean close) {
        return convertIfTag(false, fieldInfo, prefix, close);
    }

    protected String convertIfTag(TableFieldInfo fieldInfo, boolean close) {
        return convertIfTag(fieldInfo, null, close);
    }

    /**
     * 查询
     */
    public MappedStatement addSelectMappedStatement(Class<?> mapperClass, String id, SqlSource sqlSource, Class<?> resultType,
                                                    TableInfo table) {
        if (null != table) {
            String resultMap = table.getResultMap();
            if (null != resultMap) {
                /** 返回 resultMap 映射结果集 */
                return this.addMappedStatement(mapperClass, id, sqlSource, SqlCommandType.SELECT, null, resultMap, null,
                    new NoKeyGenerator(), null, null);
            }
        }

        /** 普通查询 */
        return this.addMappedStatement(mapperClass, id, sqlSource, SqlCommandType.SELECT, null, null, resultType,
            new NoKeyGenerator(), null, null);
    }

    /**
     * 插入
     */
    public MappedStatement addInsertMappedStatement(Class<?> mapperClass, Class<?> modelClass, String id, SqlSource sqlSource,
                                                    KeyGenerator keyGenerator, String keyProperty, String keyColumn) {
        return this.addMappedStatement(mapperClass, id, sqlSource, SqlCommandType.INSERT, modelClass, null, Integer.class,
            keyGenerator, keyProperty, keyColumn);
    }

    /**
     * 删除
     */
    public MappedStatement addDeleteMappedStatement(Class<?> mapperClass, String id, SqlSource sqlSource) {
        return this.addMappedStatement(mapperClass, id, sqlSource, SqlCommandType.DELETE, null, null, Integer.class,
            new NoKeyGenerator(), null, null);
    }

    /**
     * 更新
     */
    public MappedStatement addUpdateMappedStatement(Class<?> mapperClass, Class<?> modelClass, String id, SqlSource sqlSource) {
        return this.addMappedStatement(mapperClass, id, sqlSource, SqlCommandType.UPDATE, modelClass, null, Integer.class,
            new NoKeyGenerator(), null, null);
    }

    public MappedStatement addMappedStatement(Class<?> mapperClass, String id, SqlSource sqlSource,
                                              SqlCommandType sqlCommandType, Class<?> parameterClass, String resultMap, Class<?> resultType,
                                              KeyGenerator keyGenerator, String keyProperty, String keyColumn) {
        String statementName = mapperClass.getName() + "." + id;
        if (hasMappedStatement(statementName)) {
            System.err.println("{" + statementName
                + "} Has been loaded by XML or SqlProvider, ignoring the injection of the SQL.");
            return null;
        }
        /** 缓存逻辑处理 */
        boolean isSelect = false;
        if (sqlCommandType == SqlCommandType.SELECT) {
            isSelect = true;
        }
        return builderAssistant.addMappedStatement(id, sqlSource, StatementType.PREPARED, sqlCommandType, null, null, null,
            parameterClass, resultMap, resultType, null, !isSelect, isSelect, false, keyGenerator, keyProperty, keyColumn,
            configuration.getDatabaseId(), languageDriver, null);
    }

    // --------------------------------------------------------SqlRunner------------------------------------------------------------

    @Override
    public void injectSqlRunner(Configuration configuration) {
        this.configuration = configuration;
        this.languageDriver = configuration.getDefaultScriptingLanguageInstance();
        initSelectList();
        initSelectObjs();
        initInsert();
        initUpdate();
        initDelete();
        initCount();
    }

    /**
     * 是否已经存在MappedStatement
     *
     * @param mappedStatement
     * @return
     */
    private boolean hasMappedStatement(String mappedStatement) {
        return configuration.hasStatement(mappedStatement, false);
    }

    /**
     * 创建查询MappedStatement
     *
     * @param mappedStatement
     * @param sqlSource       执行的sqlSource
     * @param resultType      返回的结果类型
     */
    @SuppressWarnings("serial")
    private void createSelectMappedStatement(String mappedStatement, SqlSource sqlSource, final Class<?> resultType) {
        MappedStatement ms = new MappedStatement.Builder(configuration, mappedStatement, sqlSource, SqlCommandType.SELECT)
            .resultMaps(new ArrayList<ResultMap>() {
                {
                    add(new ResultMap.Builder(configuration, "defaultResultMap", resultType, new ArrayList<ResultMapping>(0))
                        .build());
                }
            }).build();
        // 缓存
        configuration.addMappedStatement(ms);
    }

    /**
     * 创建一个MappedStatement
     *
     * @param mappedStatement
     * @param sqlSource       执行的sqlSource
     * @param sqlCommandType  执行的sqlCommandType
     */
    @SuppressWarnings("serial")
    private void createUpdateMappedStatement(String mappedStatement, SqlSource sqlSource, SqlCommandType sqlCommandType) {
        MappedStatement ms = new MappedStatement.Builder(configuration, mappedStatement, sqlSource, sqlCommandType).resultMaps(
            new ArrayList<ResultMap>() {
                {
                    add(new ResultMap.Builder(configuration, "defaultResultMap", int.class, new ArrayList<ResultMapping>(0))
                        .build());
                }
            }).build();
        // 缓存
        configuration.addMappedStatement(ms);
    }

    /**
     * initSelectList
     */
    private void initSelectList() {
        if (hasMappedStatement(SqlRunner.SELECT_LIST)) {
            logger.warn("MappedStatement 'SqlRunner.SelectList' Already Exists");
            return;
        }
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, SqlRunner.SQLScript, Map.class);
        createSelectMappedStatement(SqlRunner.SELECT_LIST, sqlSource, Map.class);
    }

    /**
     * initSelectObjs
     */
    private void initSelectObjs() {
        if (hasMappedStatement(SqlRunner.SELECT_OBJS)) {
            logger.warn("MappedStatement 'SqlRunner.SelectObjs' Already Exists");
            return;
        }
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, SqlRunner.SQLScript, Object.class);
        createSelectMappedStatement(SqlRunner.SELECT_OBJS, sqlSource, Object.class);
    }

    /**
     * initCount
     */
    private void initCount() {
        if (hasMappedStatement(SqlRunner.COUNT)) {
            logger.warn("MappedStatement 'SqlRunner.Count' Already Exists");
            return;
        }
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, SqlRunner.SQLScript, Map.class);
        createSelectMappedStatement(SqlRunner.COUNT, sqlSource, Integer.class);
    }

    /**
     * initInsert
     */
    private void initInsert() {
        if (hasMappedStatement(SqlRunner.INSERT)) {
            logger.warn("MappedStatement 'SqlRunner.Insert' Already Exists");
            return;
        }
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, SqlRunner.SQLScript, Map.class);
        createUpdateMappedStatement(SqlRunner.INSERT, sqlSource, SqlCommandType.INSERT);
    }

    /**
     * initUpdate
     */
    private void initUpdate() {
        if (hasMappedStatement(SqlRunner.UPDATE)) {
            logger.warn("MappedStatement 'SqlRunner.Update' Already Exists");
            return;
        }
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, SqlRunner.SQLScript, Map.class);
        createUpdateMappedStatement(SqlRunner.UPDATE, sqlSource, SqlCommandType.UPDATE);
    }

    /**
     * initDelete
     */
    private void initDelete() {
        if (hasMappedStatement(SqlRunner.DELETE)) {
            logger.warn("MappedStatement 'SqlRunner.Delete' Already Exists");
            return;
        }
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, SqlRunner.SQLScript, Map.class);
        createUpdateMappedStatement(SqlRunner.DELETE, sqlSource, SqlCommandType.DELETE);
    }

    /**
     * <p>
     * 全局配置
     * </p>
     */
    protected GlobalConfiguration getGlobalConfig() {
        return GlobalConfigUtils.getGlobalConfig(configuration);
    }

}
View Code

可以看到MybatisSqlSessionFactoryBean #afterPropertiesSet 方法(Bean 生命周期的InitializingBean 初始化阶段) 调用buildSqlSessionFactory 方法开始执行Mybatis的初始化启动过程。最终经过下面调用: 可以看到走的调用都是MybatisPlus 重写之后的方法

1》 com.baomidou.mybatisplus.MybatisMapperAnnotationBuilder#parse 如下:

    public void parse() {
        String resource = this.type.toString();
        if (!this.configuration.isResourceLoaded(resource)) {
            this.loadXmlResource();
            this.configuration.addLoadedResource(resource);
            this.assistant.setCurrentNamespace(this.type.getName());
            this.parseCache();
            this.parseCacheRef();
            Method[] methods = this.type.getMethods();
            if (BaseMapper.class.isAssignableFrom(this.type)) {
                GlobalConfigUtils.getSqlInjector(this.configuration).inspectInject(this.assistant, this.type);
            }

            Method[] arr$ = methods;
            int len$ = methods.length;

            for(int i$ = 0; i$ < len$; ++i$) {
                Method method = arr$[i$];

                try {
                    if (!method.isBridge()) {
                        this.parseStatement(method);
                    }
                } catch (IncompleteElementException var8) {
                    this.configuration.addIncompleteMethod(new MethodResolver(this, method));
                }
            }
        }

        this.parsePendingMethods();
    }

 com.baomidou.mybatisplus.toolkit.GlobalConfigUtils#getSqlInjector 获取SQL注入器

    public static ISqlInjector getSqlInjector(Configuration configuration) {
        GlobalConfiguration globalConfiguration = getGlobalConfig(configuration);
        ISqlInjector sqlInjector = globalConfiguration.getSqlInjector();
        if (sqlInjector == null) {
            sqlInjector = new AutoSqlInjector();
            globalConfiguration.setSqlInjector((ISqlInjector)sqlInjector);
        }

        return (ISqlInjector)sqlInjector;
    }

  可以看到是拿设置在GlobalConfiguration 中的SQL注入器,如果没拿到会拿默认的AutoSqlInjector 并设置到GlobalConfiguration 对象属性中。

2》com.baomidou.mybatisplus.mapper.AutoSqlInjector#inject(org.apache.ibatis.builder.MapperBuilderAssistant, java.lang.Class<?>) 如下:

    /**
     * 注入单点 crudSql
     */
    @Override
    public void inject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass) {
        this.configuration = builderAssistant.getConfiguration();
        this.builderAssistant = builderAssistant;
        this.languageDriver = configuration.getDefaultScriptingLanguageInstance();

        //去除 驼峰设置 PLUS 配置 > 原生配置 (该配置不需要与原生Mybatis混淆)
        /*if (!globalCache.isDbColumnUnderline()) {
            globalCache.setDbColumnUnderline(configuration.isMapUnderscoreToCamelCase());
        }*/
        Class<?> modelClass = extractModelClass(mapperClass);
        if (null != modelClass) {
            /**
             * 初始化 SQL 解析
             */
            if (this.getGlobalConfig().isSqlParserCache()) {
                PluginUtils.initSqlParserInfoCache(mapperClass);
            }
            TableInfo table = TableInfoHelper.initTableInfo(builderAssistant, modelClass);
            injectSql(builderAssistant, mapperClass, modelClass, table);
        }
    }


    /**
     * 提取泛型模型,多泛型的时候请将泛型T放在第一位
     *
     * @param mapperClass
     * @return
     */
    protected Class<?> extractModelClass(Class<?> mapperClass) {
        Type[] types = mapperClass.getGenericInterfaces();
        ParameterizedType target = null;
        for (Type type : types) {
            if (type instanceof ParameterizedType) {
                Type[] typeArray = ((ParameterizedType) type).getActualTypeArguments();
                if (ArrayUtils.isNotEmpty(typeArray)) {
                    for (Type t : typeArray) {
                        if (t instanceof TypeVariable || t instanceof WildcardType) {
                            target = null;
                            break;
                        } else {
                            target = (ParameterizedType) type;
                            break;
                        }
                    }
                }
                break;
            }
        }
        return target == null ? null : (Class<?>) target.getActualTypeArguments()[0];
    }

  可以看到是先获取到接口上的泛型的实际类型,也就是我们的Bean 的类型,然后根据Bean 的类型调用 com.baomidou.mybatisplus.toolkit.TableInfoHelper#initTableInfo 构造成Table 信息,com.baomidou.mybatisplus.toolkit.TableInfoHelper 源码如下:(extractModelClass 也可以作为一个工具类作为抽取泛型实际类型)

/**
package com.baomidou.mybatisplus.toolkit;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.ibatis.builder.MapperBuilderAssistant;
import org.apache.ibatis.executor.keygen.KeyGenerator;
import org.apache.ibatis.executor.keygen.NoKeyGenerator;
import org.apache.ibatis.executor.keygen.SelectKeyGenerator;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.mapping.StatementType;
import org.apache.ibatis.scripting.LanguageDriver;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSessionFactory;

import com.baomidou.mybatisplus.annotations.KeySequence;
import com.baomidou.mybatisplus.annotations.TableField;
import com.baomidou.mybatisplus.annotations.TableId;
import com.baomidou.mybatisplus.annotations.TableName;
import com.baomidou.mybatisplus.entity.GlobalConfiguration;
import com.baomidou.mybatisplus.entity.TableFieldInfo;
import com.baomidou.mybatisplus.entity.TableInfo;
import com.baomidou.mybatisplus.enums.IdType;
import com.baomidou.mybatisplus.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.incrementer.IKeyGenerator;
import com.baomidou.mybatisplus.mapper.SqlRunner;

/**
 * <p>
 * 实体类反射表辅助类
 * </p>
 *
 * @author hubin sjy
 * @Date 2016-09-09
 */
public class TableInfoHelper {

    private static final Log logger = LogFactory.getLog(TableInfoHelper.class);
    /**
     * 缓存反射类表信息
     */
    private static final Map<String, TableInfo> tableInfoCache = new ConcurrentHashMap<>();
    /**
     * 默认表主键
     */
    private static final String DEFAULT_ID_NAME = "id";

    /**
     * <p>
     * 获取实体映射表信息
     * <p>
     *
     * @param clazz 反射实体类
     * @return
     */
    public static TableInfo getTableInfo(Class<?> clazz) {
        return tableInfoCache.get(ClassUtils.getUserClass(clazz).getName());
    }

    /**
     * <p>
     * 获取所有实体映射表信息
     * <p>
     *
     * @return
     */
    public static List<TableInfo> getTableInfos() {
        return new ArrayList<>(tableInfoCache.values());
    }

    /**
     * <p>
     * 实体类反射获取表信息【初始化】
     * <p>
     *
     * @param clazz 反射实体类
     * @return
     */
    public synchronized static TableInfo initTableInfo(MapperBuilderAssistant builderAssistant, Class<?> clazz) {
        TableInfo tableInfo = tableInfoCache.get(clazz.getName());
        if (StringUtils.checkValNotNull(tableInfo)) {
            if (StringUtils.checkValNotNull(builderAssistant)) {
                tableInfo.setConfigMark(builderAssistant.getConfiguration());
            }
            return tableInfo;
        }
        tableInfo = new TableInfo();
        GlobalConfiguration globalConfig;
        if (null != builderAssistant) {
            tableInfo.setCurrentNamespace(builderAssistant.getCurrentNamespace());
            tableInfo.setConfigMark(builderAssistant.getConfiguration());
            globalConfig = GlobalConfigUtils.getGlobalConfig(builderAssistant.getConfiguration());
        } else {
            // 兼容测试场景
            globalConfig = GlobalConfigUtils.DEFAULT;
        }
        /* 表名 */
        TableName table = clazz.getAnnotation(TableName.class);
        String tableName = clazz.getSimpleName();
        if (table != null && StringUtils.isNotEmpty(table.value())) {
            tableName = table.value();
        } else {
            // 开启字段下划线申明
            if (globalConfig.isDbColumnUnderline()) {
                tableName = StringUtils.camelToUnderline(tableName);
            }
            // 大写命名判断
            if (globalConfig.isCapitalMode()) {
                tableName = tableName.toUpperCase();
            } else {
                // 首字母小写
                tableName = StringUtils.firstToLowerCase(tableName);
            }
            // 存在表前缀
            if (null != globalConfig.getTablePrefix()) {
                tableName = globalConfig.getTablePrefix() + tableName;
            }
        }
        tableInfo.setTableName(tableName);

        // 开启了自定义 KEY 生成器
        if (null != globalConfig.getKeyGenerator()) {
            tableInfo.setKeySequence(clazz.getAnnotation(KeySequence.class));
        }

        /* 表结果集映射 */
        if (table != null && StringUtils.isNotEmpty(table.resultMap())) {
            tableInfo.setResultMap(table.resultMap());
        }
        List<TableFieldInfo> fieldList = new ArrayList<>();
        List<Field> list = getAllFields(clazz);
        // 标记是否读取到主键
        boolean isReadPK = false;
        boolean existTableId = existTableId(list);
        for (Field field : list) {
            /*
             * 主键ID 初始化
             */
            if (!isReadPK) {
                if (existTableId) {
                    isReadPK = initTableId(globalConfig, tableInfo, field, clazz);
                } else {
                    isReadPK = initFieldId(globalConfig, tableInfo, field, clazz);
                }
                if (isReadPK) {
                    continue;
                }

            }
            /*
             * 字段初始化
             */
            if (initTableField(globalConfig, tableInfo, fieldList, field, clazz)) {
                continue;
            }

            /*
             * 字段, 使用 camelToUnderline 转换驼峰写法为下划线分割法, 如果已指定 TableField , 便不会执行这里
             */
            fieldList.add(new TableFieldInfo(globalConfig, tableInfo, field));
        }

        /* 字段列表 */
        tableInfo.setFieldList(globalConfig, fieldList);
        /*
         * 未发现主键注解,提示警告信息
         */
        if (StringUtils.isEmpty(tableInfo.getKeyColumn())) {
            logger.warn(String.format("Warn: Could not find @TableId in Class: %s.", clazz.getName()));
        }
        /*
         * 注入
         */
        tableInfoCache.put(clazz.getName(), tableInfo);
        return tableInfo;
    }

    /**
     * <p>
     * 判断主键注解是否存在
     * </p>
     *
     * @param list 字段列表
     * @return
     */
    public static boolean existTableId(List<Field> list) {
        boolean exist = false;
        for (Field field : list) {
            TableId tableId = field.getAnnotation(TableId.class);
            if (tableId != null) {
                exist = true;
                break;
            }
        }
        return exist;
    }

    /**
     * <p>
     * 主键属性初始化
     * </p>
     *
     * @param tableInfo
     * @param field
     * @param clazz
     * @return true 继续下一个属性判断,返回 continue;
     */
    private static boolean initTableId(GlobalConfiguration globalConfig, TableInfo tableInfo, Field field, Class<?> clazz) {
        TableId tableId = field.getAnnotation(TableId.class);
        if (tableId != null) {
            if (StringUtils.isEmpty(tableInfo.getKeyColumn())) {
                /*
                 * 主键策略( 注解 > 全局 > 默认 )
                 */
                // 设置 Sequence 其他策略无效
                if (IdType.NONE != tableId.type()) {
                    tableInfo.setIdType(tableId.type());
                } else {
                    tableInfo.setIdType(globalConfig.getIdType());
                }

                /* 字段 */
                String column = field.getName();
                if (StringUtils.isNotEmpty(tableId.value())) {
                    column = tableId.value();
                    tableInfo.setKeyRelated(true);
                } else {
                    // 开启字段下划线申明
                    if (globalConfig.isDbColumnUnderline()) {
                        column = StringUtils.camelToUnderline(column);
                        tableInfo.setKeyRelated(true);
                    }
                    // 全局大写命名
                    if (globalConfig.isCapitalMode()) {
                        column = column.toUpperCase();
                    }
                }
                tableInfo.setKeyColumn(column);
                tableInfo.setKeyProperty(field.getName());
                return true;
            } else {
                throwExceptionId(clazz);
            }
        }
        return false;
    }

    /**
     * <p>
     * 主键属性初始化
     * </p>
     *
     * @param tableInfo
     * @param field
     * @param clazz
     * @return true 继续下一个属性判断,返回 continue;
     */
    private static boolean initFieldId(GlobalConfiguration globalConfig, TableInfo tableInfo, Field field, Class<?> clazz) {
        String column = field.getName();
        if (globalConfig.isCapitalMode()) {
            column = column.toUpperCase();
        }
        if (DEFAULT_ID_NAME.equalsIgnoreCase(column)) {
            if (StringUtils.isEmpty(tableInfo.getKeyColumn())) {
                tableInfo.setIdType(globalConfig.getIdType());
                tableInfo.setKeyColumn(column);
                tableInfo.setKeyProperty(field.getName());
                return true;
            } else {
                throwExceptionId(clazz);
            }
        }
        return false;
    }

    /**
     * <p>
     * 发现设置多个主键注解抛出异常
     * </p>
     */
    private static void throwExceptionId(Class<?> clazz) {
        StringBuilder errorMsg = new StringBuilder();
        errorMsg.append("There must be only one, Discover multiple @TableId annotation in ");
        errorMsg.append(clazz.getName());
        throw new MybatisPlusException(errorMsg.toString());
    }

    /**
     * <p>
     * 字段属性初始化
     * </p>
     *
     * @param globalConfig 全局配置
     * @param tableInfo    表信息
     * @param fieldList    字段列表
     * @param clazz        当前表对象类
     * @return true 继续下一个属性判断,返回 continue;
     */
    private static boolean initTableField(GlobalConfiguration globalConfig, TableInfo tableInfo, List<TableFieldInfo> fieldList,
                                          Field field, Class<?> clazz) {
        /* 获取注解属性,自定义字段 */
        TableField tableField = field.getAnnotation(TableField.class);
        if (tableField != null) {
            String columnName = field.getName();
            if (StringUtils.isNotEmpty(tableField.value())) {
                columnName = tableField.value();
            }
            /*
             * el 语法支持,可以传入多个参数以逗号分开
             */
            String el = field.getName();
            if (StringUtils.isNotEmpty(tableField.el())) {
                el = tableField.el();
            }
            String[] columns = columnName.split(";");
            String[] els = el.split(";");
            if (columns.length == els.length) {
                for (int i = 0; i < columns.length; i++) {
                    fieldList.add(new TableFieldInfo(globalConfig, tableInfo, columns[i], els[i], field, tableField));
                }
            } else {
                String errorMsg = "Class: %s, Field: %s, 'value' 'el' Length must be consistent.";
                throw new MybatisPlusException(String.format(errorMsg, clazz.getName(), field.getName()));
            }
            return true;
        }
        return false;
    }

    /**
     * 获取该类的所有属性列表
     *
     * @param clazz 反射类
     * @return
     */
    public static List<Field> getAllFields(Class<?> clazz) {
        List<Field> fieldList = ReflectionKit.getFieldList(ClassUtils.getUserClass(clazz));
        if (CollectionUtils.isNotEmpty(fieldList)) {
            Iterator<Field> iterator = fieldList.iterator();
            while (iterator.hasNext()) {
                Field field = iterator.next();
                /* 过滤注解非表字段属性 */
                TableField tableField = field.getAnnotation(TableField.class);
                if (tableField != null && !tableField.exist()) {
                    iterator.remove();
                }
            }
        }
        return fieldList;
    }

    /**
     * 初始化SqlSessionFactory (供Mybatis原生调用)
     *
     * @param sqlSessionFactory
     * @return
     */
    public static void initSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
        Configuration configuration = sqlSessionFactory.getConfiguration();
        GlobalConfiguration globalConfig = GlobalConfigUtils.getGlobalConfig(configuration);
        // SqlRunner
        SqlRunner.FACTORY = sqlSessionFactory;
        if (globalConfig == null) {
            GlobalConfiguration defaultCache = GlobalConfigUtils.defaults();
            defaultCache.setSqlSessionFactory(sqlSessionFactory);
            GlobalConfigUtils.setGlobalConfig(configuration, defaultCache);
        } else {
            globalConfig.setSqlSessionFactory(sqlSessionFactory);
        }
    }

    /**
     * <p>
     * 自定义 KEY 生成器
     * </p>
     */
    public static KeyGenerator genKeyGenerator(TableInfo tableInfo, MapperBuilderAssistant builderAssistant,
                                               String baseStatementId, LanguageDriver languageDriver) {
        IKeyGenerator keyGenerator = GlobalConfigUtils.getKeyGenerator(builderAssistant.getConfiguration());
        if (null == keyGenerator) {
            throw new IllegalArgumentException("not configure IKeyGenerator implementation class.");
        }
        String id = baseStatementId + SelectKeyGenerator.SELECT_KEY_SUFFIX;
        Class<?> resultTypeClass = tableInfo.getKeySequence().clazz();
        StatementType statementType = StatementType.PREPARED;
        String keyProperty = tableInfo.getKeyProperty();
        String keyColumn = tableInfo.getKeyColumn();
        SqlSource sqlSource = languageDriver.createSqlSource(builderAssistant.getConfiguration(),
            keyGenerator.executeSql(tableInfo.getKeySequence().value()), null);
        builderAssistant.addMappedStatement(id, sqlSource, statementType, SqlCommandType.SELECT, null, null, null,
            null, null, resultTypeClass, null, false, false, false,
            new NoKeyGenerator(), keyProperty, keyColumn, null, languageDriver, null);
        id = builderAssistant.applyCurrentNamespace(id, false);
        MappedStatement keyStatement = builderAssistant.getConfiguration().getMappedStatement(id, false);
        SelectKeyGenerator selectKeyGenerator = new SelectKeyGenerator(keyStatement, true);
        builderAssistant.getConfiguration().addKeyGenerator(id, selectKeyGenerator);
        return selectKeyGenerator;
    }

}
View Code

最终获取到的TableInfo 如下:(相当于一个抽取注解信息记录类)

 3》 接着调用com.baomidou.mybatisplus.mapper.AutoSqlInjector#injectSql 开始注入SQL

    /**
     * <p>
     * 注入SQL
     * </p>
     *
     * @param builderAssistant
     * @param mapperClass
     * @param modelClass
     * @param table
     */
    protected void injectSql(MapperBuilderAssistant builderAssistant, Class<?> mapperClass, Class<?> modelClass, TableInfo table) {
        /**
         * #148 表信息包含主键,注入主键相关方法
         */
        if (StringUtils.isNotEmpty(table.getKeyProperty())) {
            /** 删除 */
            this.injectDeleteByIdSql(false, mapperClass, modelClass, table);
            this.injectDeleteByIdSql(true, mapperClass, modelClass, table);
            /** 修改 */
            this.injectUpdateByIdSql(true, mapperClass, modelClass, table);
            this.injectUpdateByIdSql(false, mapperClass, modelClass, table);
            /** 查询 */
            this.injectSelectByIdSql(false, mapperClass, modelClass, table);
            this.injectSelectByIdSql(true, mapperClass, modelClass, table);
        } else {
            // 表不包含主键时 给予警告
            logger.warn(String.format("%s ,Not found @TableId annotation, Cannot use Mybatis-Plus 'xxById' Method.",
                modelClass.toString()));
        }
        /**
         * 正常注入无需主键方法
         */
        /** 插入 */
        this.injectInsertOneSql(true, mapperClass, modelClass, table);
        this.injectInsertOneSql(false, mapperClass, modelClass, table);
        /** 删除 */
        this.injectDeleteSql(mapperClass, modelClass, table);
        this.injectDeleteByMapSql(mapperClass, table);
        /** 修改 */
        this.injectUpdateSql(mapperClass, modelClass, table);
        /** 修改 (自定义 set 属性) */
        this.injectUpdateForSetSql(mapperClass, modelClass, table);
        /** 查询 */
        this.injectSelectByMapSql(mapperClass, modelClass, table);
        this.injectSelectOneSql(mapperClass, modelClass, table);
        this.injectSelectCountSql(mapperClass, modelClass, table);
        this.injectSelectListSql(SqlMethod.SELECT_LIST, mapperClass, modelClass, table);
        this.injectSelectListSql(SqlMethod.SELECT_PAGE, mapperClass, modelClass, table);
        this.injectSelectMapsSql(SqlMethod.SELECT_MAPS, mapperClass, modelClass, table);
        this.injectSelectMapsSql(SqlMethod.SELECT_MAPS_PAGE, mapperClass, modelClass, table);
        this.injectSelectObjsSql(SqlMethod.SELECT_OBJS, mapperClass, modelClass, table);
        /** 自定义方法 */
        this.inject(configuration, builderAssistant, mapperClass, modelClass, table);
    }

    /**
     * 自定义方法,注入点(子类需重写该方法)
     */
    public void inject(Configuration configuration, MapperBuilderAssistant builderAssistant, Class<?> mapperClass,
                       Class<?> modelClass, TableInfo table) {
        // to do nothing
    }
  • 首先注入根据ID增删改查相关的方法
  • 然后注入一些非注解的方法
  • 调用com.baomidou.mybatisplus.mapper.AutoSqlInjector#inject(org.apache.ibatis.session.Configuration, org.apache.ibatis.builder.MapperBuilderAssistant, java.lang.Class<?>, java.lang.Class<?>, com.baomidou.mybatisplus.entity.TableInfo) 交由子类覆盖自定义注入的方法。

4》以注入根据ID删除为例子进行分析,调用了两次,传入的参数只有第一个有区别(分别代表批量和单个),查看com.baomidou.mybatisplus.mapper.AutoSqlInjector#injectDeleteByIdSql:

    protected void injectDeleteByIdSql(boolean batch, Class<?> mapperClass, Class<?> modelClass, TableInfo table) {
        SqlMethod sqlMethod = SqlMethod.DELETE_BY_ID;
        SqlSource sqlSource;
        // 因为后面要通过get方法获取类型,所以这里要获取key的属性值
        String idStr = table.getKeyProperty();
        if (batch) {
            sqlMethod = SqlMethod.DELETE_BATCH_BY_IDS;
            StringBuilder ids = new StringBuilder();
            ids.append("
<foreach item="item" index="index" collection="coll" separator=",">");
            ids.append("#{item}");
            ids.append("
</foreach>");
            idStr = ids.toString();
        }
        String sql = String.format(sqlMethod.getSql(), table.getTableName(), table.getKeyColumn(), idStr);
        sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
        this.addDeleteMappedStatement(mapperClass, sqlMethod.getMethod(), sqlSource);
    }

    public MappedStatement addDeleteMappedStatement(Class<?> mapperClass, String id, SqlSource sqlSource) {
        return this.addMappedStatement(mapperClass, id, sqlSource, SqlCommandType.DELETE, null, null, Integer.class,
            new NoKeyGenerator(), null, null);
    }

    public MappedStatement addMappedStatement(Class<?> mapperClass, String id, SqlSource sqlSource,
                                              SqlCommandType sqlCommandType, Class<?> parameterClass, String resultMap, Class<?> resultType,
                                              KeyGenerator keyGenerator, String keyProperty, String keyColumn) {
        String statementName = mapperClass.getName() + "." + id;
        if (hasMappedStatement(statementName)) {
            System.err.println("{" + statementName
                + "} Has been loaded by XML or SqlProvider, ignoring the injection of the SQL.");
            return null;
        }
        /** 缓存逻辑处理 */
        boolean isSelect = false;
        if (sqlCommandType == SqlCommandType.SELECT) {
            isSelect = true;
        }
        return builderAssistant.addMappedStatement(id, sqlSource, StatementType.PREPARED, sqlCommandType, null, null, null,
            parameterClass, resultMap, resultType, null, !isSelect, isSelect, false, keyGenerator, keyProperty, keyColumn,
            configuration.getDatabaseId(), languageDriver, null);
    }

  可以看到先选择对应的SqlMethod 模板,然后根据是否是批量生成不同的id 模板,然后格式化为最后的sql , 最后根据namespace 和 方法名称作为MappedStatement 的ID调用builderAssistant.addMappedStatement 添加到Configuration 对象内部缓存起来。

  例如一个delete语句最后生成的两个sql如下:

单个:

<script>DELETE FROM department WHERE id=#{id}</script>

批量:

<script>DELETE FROM department WHERE id IN (
<foreach item="item" index="index" collection="coll" separator=",">#{item}
</foreach>)</script>

com.baomidou.mybatisplus.enums.SqlMethod源码如下:里面包含所有的方法名称以及描述

package com.baomidou.mybatisplus.enums;

/**
 * <p>
 * MybatisPlus 支持 SQL 方法
 * </p>
 *
 * @author hubin
 * @Date 2016-01-23
 */
public enum SqlMethod {
    /**
     * 插入
     */
    INSERT_ONE("insert", "插入一条数据(选择字段插入)", "<script>INSERT INTO %s %s VALUES %s</script>"),
    INSERT_ONE_ALL_COLUMN("insertAllColumn", "插入一条数据(全部字段插入)", "<script>INSERT INTO %s %s VALUES %s</script>"),

    /**
     * 删除
     */
    DELETE_BY_ID("deleteById", "根据ID 删除一条数据", "<script>DELETE FROM %s WHERE %s=#{%s}</script>"),
    DELETE_BY_MAP("deleteByMap", "根据columnMap 条件删除记录", "<script>DELETE FROM %s %s</script>"),
    DELETE("delete", "根据 entity 条件删除记录", "<script>DELETE FROM %s %s</script>"),
    DELETE_BATCH_BY_IDS("deleteBatchIds", "根据ID集合,批量删除数据", "<script>DELETE FROM %s WHERE %s IN (%s)</script>"),

    /**
     * 逻辑删除
     */
    LOGIC_DELETE_BY_ID("deleteById", "根据ID 逻辑删除一条数据", "<script>UPDATE %s %s WHERE %s=#{%s}</script>"),
    LOGIC_DELETE_BY_MAP("deleteByMap", "根据columnMap 条件逻辑删除记录", "<script>UPDATE %s %s %s</script>"),
    LOGIC_DELETE("delete", "根据 entity 条件逻辑删除记录", "<script>UPDATE %s %s %s</script>"),
    LOGIC_DELETE_BATCH_BY_IDS("deleteBatchIds", "根据ID集合,批量逻辑删除数据", "<script>UPDATE %s %s WHERE %s IN (%s)</script>"),

    /**
     * 修改
     */
    UPDATE_BY_ID("updateById", "根据ID 选择修改数据", "<script>UPDATE %s %s WHERE %s=#{%s} %s</script>"),
    UPDATE_ALL_COLUMN_BY_ID("updateAllColumnById", "根据ID 修改全部数据", "<script>UPDATE %s %s WHERE %s=#{%s} %s</script>"),
    UPDATE("update", "根据 whereEntity 条件,更新记录", "<script>UPDATE %s %s %s</script>"),
    UPDATE_FOR_SET("updateForSet", "根据 whereEntity 条件,自定义Set值更新记录", "<script>UPDATE %s %s %s</script>"),

    /**
     * 逻辑删除 -> 修改
     */
    LOGIC_UPDATE_BY_ID("updateById", "根据ID 修改数据", "<script>UPDATE %s %s WHERE %s=#{%s} %s</script>"),
    LOGIC_UPDATE_ALL_COLUMN_BY_ID("updateAllColumnById", "根据ID 选择修改数据", "<script>UPDATE %s %s WHERE %s=#{%s} %s</script>"),


    /**
     * 查询
     */
    SELECT_BY_ID("selectById", "根据ID 查询一条数据", "SELECT %s FROM %s WHERE %s=#{%s}"),
    SELECT_BY_MAP("selectByMap", "根据columnMap 查询一条数据", "<script>SELECT %s FROM %s %s</script>"),
    SELECT_BATCH_BY_IDS("selectBatchIds", "根据ID集合,批量查询数据", "<script>SELECT %s FROM %s WHERE %s IN (%s)</script>"),
    SELECT_ONE("selectOne", "查询满足条件一条数据", "<script>SELECT %s FROM %s %s</script>"),
    SELECT_COUNT("selectCount", "查询满足条件总记录数", "<script>SELECT COUNT(1) FROM %s %s</script>"),
    SELECT_LIST("selectList", "查询满足条件所有数据", "<script>SELECT %s FROM %s %s</script>"),
    SELECT_PAGE("selectPage", "查询满足条件所有数据(并翻页)", "<script>SELECT %s FROM %s %s</script>"),
    SELECT_MAPS("selectMaps", "查询满足条件所有数据", "<script>SELECT %s FROM %s %s</script>"),
    SELECT_MAPS_PAGE("selectMapsPage", "查询满足条件所有数据(并翻页)", "<script>SELECT %s FROM %s %s</script>"),
    SELECT_OBJS("selectObjs", "查询满足条件所有数据", "<script>SELECT %s FROM %s %s</script>"),

    /**
     * 逻辑删除 -> 查询
     */
    LOGIC_SELECT_BY_ID("selectById", "根据ID 查询一条数据", "SELECT %s FROM %s WHERE %s=#{%s} %s"),
    LOGIC_SELECT_BATCH_BY_IDS("selectBatchIds", "根据ID集合,批量查询数据", "<script>SELECT %s FROM %s WHERE %s IN (%s) %s</script>");

    private final String method;
    private final String desc;
    private final String sql;

    SqlMethod(final String method, final String desc, final String sql) {
        this.method = method;
        this.desc = desc;
        this.sql = sql;
    }

    public String getMethod() {
        return this.method;
    }

    public String getDesc() {
        return this.desc;
    }

    public String getSql() {
        return this.sql;
    }

}
View Code

5》 可以看到上面3》 结束之后会生成一堆自定义的sql模板,并且生成MappedStatement 对象存到Configuration 对象中。

其对应的就是com.baomidou.mybatisplus.mapper.BaseMapper 接口里面的一些方法, 所以我们自己的Mapper 需要继承该接口,这样调用的时候会根据类全路径加方法名称生成MappedStatement的id,这样来找到上面3》 中生成的MappedStatement 对象。

com.baomidou.mybatisplus.mapper.BaseMapper 接口如下: 下面接口对应的SQL会通过上面步骤进行自动生成。

package com.baomidou.mybatisplus.mapper;

import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.session.RowBounds;

/**
 * <p>
 * Mapper 继承该接口后,无需编写 mapper.xml 文件,即可获得CRUD功能
 * </p>
 * <p>
 * 这个 Mapper 支持 id 泛型
 * </p>
 *
 * @author hubin
 * @Date 2016-01-23
 */
public interface BaseMapper<T> {

    /**
     * <p>
     * 插入一条记录
     * </p>
     *
     * @param entity 实体对象
     * @return int
     */
    Integer insert(T entity);

    /**
     * <p>
     * 插入一条记录
     * </p>
     *
     * @param entity 实体对象
     * @return int
     */
    Integer insertAllColumn(T entity);

    /**
     * <p>
     * 根据 ID 删除
     * </p>
     *
     * @param id 主键ID
     * @return int
     */
    Integer deleteById(Serializable id);

    /**
     * <p>
     * 根据 columnMap 条件,删除记录
     * </p>
     *
     * @param columnMap 表字段 map 对象
     * @return int
     */
    Integer deleteByMap(@Param("cm") Map<String, Object> columnMap);

    /**
     * <p>
     * 根据 entity 条件,删除记录
     * </p>
     *
     * @param wrapper 实体对象封装操作类(可以为 null)
     * @return int
     */
    Integer delete(@Param("ew") Wrapper<T> wrapper);

    /**
     * <p>
     * 删除(根据ID 批量删除)
     * </p>
     *
     * @param idList 主键ID列表
     * @return int
     */
    Integer deleteBatchIds(@Param("coll") Collection<? extends Serializable> idList);

    /**
     * <p>
     * 根据 ID 修改
     * </p>
     *
     * @param entity 实体对象
     * @return int
     */
    Integer updateById(@Param("et") T entity);

    /**
     * <p>
     * 根据 ID 修改
     * </p>
     *
     * @param entity 实体对象
     * @return int
     */
    Integer updateAllColumnById(@Param("et") T entity);

    /**
     * <p>
     * 根据 whereEntity 条件,更新记录
     * </p>
     *
     * @param entity  实体对象
     * @param wrapper 实体对象封装操作类(可以为 null)
     * @return
     */
    Integer update(@Param("et") T entity, @Param("ew") Wrapper<T> wrapper);

    /**
     * <p>
     * 根据 whereEntity 条件,更新记录
     * </p>
     *
     * @param setStr  set字符串
     * @param wrapper 实体对象封装操作类(可以为 null)
     * @return
     */
    Integer updateForSet(@Param("setStr") String setStr, @Param("ew") Wrapper<T> wrapper);

    /**
     * <p>
     * 根据 ID 查询
     * </p>
     *
     * @param id 主键ID
     * @return T
     */
    T selectById(Serializable id);

    /**
     * <p>
     * 查询(根据ID 批量查询)
     * </p>
     *
     * @param idList 主键ID列表
     * @return List<T>
     */
    List<T> selectBatchIds(@Param("coll") Collection<? extends Serializable> idList);

    /**
     * <p>
     * 查询(根据 columnMap 条件)
     * </p>
     *
     * @param columnMap 表字段 map 对象
     * @return List<T>
     */
    List<T> selectByMap(@Param("cm") Map<String, Object> columnMap);

    /**
     * <p>
     * 根据 entity 条件,查询一条记录
     * </p>
     *
     * @param entity 实体对象
     * @return T
     */
    T selectOne(@Param("ew") T entity);

    /**
     * <p>
     * 根据 Wrapper 条件,查询总记录数
     * </p>
     *
     * @param wrapper 实体对象
     * @return int
     */
    Integer selectCount(@Param("ew") Wrapper<T> wrapper);

    /**
     * <p>
     * 根据 entity 条件,查询全部记录
     * </p>
     *
     * @param wrapper 实体对象封装操作类(可以为 null)
     * @return List<T>
     */
    List<T> selectList(@Param("ew") Wrapper<T> wrapper);

    /**
     * <p>
     * 根据 Wrapper 条件,查询全部记录
     * </p>
     *
     * @param wrapper 实体对象封装操作类(可以为 null)
     * @return List<T>
     */
    List<Map<String, Object>> selectMaps(@Param("ew") Wrapper<T> wrapper);

    /**
     * <p>
     * 根据 Wrapper 条件,查询全部记录
     * 注意: 只返回第一个字段的值
     * </p>
     *
     * @param wrapper 实体对象封装操作类(可以为 null)
     * @return List<Object>
     */
    List<Object> selectObjs(@Param("ew") Wrapper<T> wrapper);

    /**
     * <p>
     * 根据 entity 条件,查询全部记录(并翻页)
     * </p>
     *
     * @param rowBounds 分页查询条件(可以为 RowBounds.DEFAULT)
     * @param wrapper   实体对象封装操作类(可以为 null)
     * @return List<T>
     */
    List<T> selectPage(RowBounds rowBounds, @Param("ew") Wrapper<T> wrapper);

    /**
     * <p>
     * 根据 Wrapper 条件,查询全部记录(并翻页)
     * </p>
     *
     * @param rowBounds 分页查询条件(可以为 RowBounds.DEFAULT)
     * @param wrapper   实体对象封装操作类
     * @return List<Map<String, Object>>
     */
    List<Map<String, Object>> selectMapsPage(RowBounds rowBounds, @Param("ew") Wrapper<T> wrapper);

}
View Code

补充:上面研究了其主要过程,下面查看其逻辑删除的主要过程。

  在上面的过程中,发现其没有对逻辑删除做处理。查看SQL注入器,是其新加了一个LogicSqlInjector, 源码如下:

/**
 * 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.mapper;

import java.util.List;
import java.util.Map;

import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.scripting.defaults.RawSqlSource;

import com.baomidou.mybatisplus.entity.TableFieldInfo;
import com.baomidou.mybatisplus.entity.TableInfo;
import com.baomidou.mybatisplus.enums.SqlMethod;
import com.baomidou.mybatisplus.toolkit.SqlReservedWords;
import com.baomidou.mybatisplus.toolkit.StringUtils;

/**
 * <p>
 * SQL 自动注入逻辑处理器<br>
 * 1、支持逻辑删除
 * </p>
 *
 * @author hubin willenfoo
 * @Date 2017-09-09
 */
public class LogicSqlInjector extends AutoSqlInjector {

    /**
     * 根据 ID 删除
     */
    @Override
    protected void injectDeleteByIdSql(boolean batch, Class<?> mapperClass, Class<?> modelClass, TableInfo table) {
        if (table.isLogicDelete()) {
            // 逻辑删除注入
            SqlMethod sqlMethod = SqlMethod.LOGIC_DELETE_BY_ID;
            SqlSource sqlSource;
            String idStr = table.getKeyProperty();
            if (batch) {
                sqlMethod = SqlMethod.LOGIC_DELETE_BATCH_BY_IDS;
                StringBuilder ids = new StringBuilder();
                ids.append("
<foreach item="item" index="index" collection="coll" separator=",">");
                ids.append("#{item}");
                ids.append("
</foreach>");
                idStr = ids.toString();
            }
            String sql = String.format(sqlMethod.getSql(), table.getTableName(), sqlLogicSet(table),
                table.getKeyColumn(), idStr);
            sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
            this.addUpdateMappedStatement(mapperClass, modelClass, sqlMethod.getMethod(), sqlSource);
        } else {
            // 正常删除
            super.injectDeleteByIdSql(batch, mapperClass, modelClass, table);
        }
    }

    /**
     * 根据 SQL 删除
     */
    @Override
    protected void injectDeleteSql(Class<?> mapperClass, Class<?> modelClass, TableInfo table) {
        if (table.isLogicDelete()) {
            // 逻辑删除注入
            SqlMethod sqlMethod = SqlMethod.LOGIC_DELETE;
            String sql = String.format(sqlMethod.getSql(), table.getTableName(), sqlLogicSet(table),
                sqlWhereEntityWrapper(table));
            SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
            this.addUpdateMappedStatement(mapperClass, modelClass, sqlMethod.getMethod(), sqlSource);
        } else {
            // 正常删除
            super.injectDeleteSql(mapperClass, modelClass, table);
        }
    }

    /**
     * 根据 MAP 删除
     */
    @Override
    protected void injectDeleteByMapSql(Class<?> mapperClass, TableInfo table) {
        if (table.isLogicDelete()) {
            // 逻辑删除注入
            SqlMethod sqlMethod = SqlMethod.LOGIC_DELETE_BY_MAP;
            String sql = String.format(sqlMethod.getSql(), table.getTableName(), sqlLogicSet(table),
                sqlWhereByMap(table));
            SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, Map.class);
            this.addUpdateMappedStatement(mapperClass, Map.class, sqlMethod.getMethod(), sqlSource);
        } else {
            // 正常删除
            super.injectDeleteByMapSql(mapperClass, table);
        }
    }

    /**
     * <p>
     * 注入查询 SQL 语句
     * </p>
     *
     * @param batch       是否为批量插入
     * @param mapperClass
     * @param modelClass
     * @param table
     */
    @Override
    protected void injectSelectByIdSql(boolean batch, Class<?> mapperClass, Class<?> modelClass, TableInfo table) {
        if (table.isLogicDelete()) {
            SqlMethod sqlMethod = SqlMethod.LOGIC_SELECT_BY_ID;
            SqlSource sqlSource;
            if (batch) {
                sqlMethod = SqlMethod.LOGIC_SELECT_BATCH_BY_IDS;
                StringBuilder ids = new StringBuilder();
                ids.append("
<foreach item="item" index="index" collection="coll" separator=",">");
                ids.append("#{item}");
                ids.append("
</foreach>");
                sqlSource = languageDriver.createSqlSource(configuration, String.format(sqlMethod.getSql(), sqlSelectColumns(table, false),
                    table.getTableName(), table.getKeyColumn(), ids.toString(), getLogicDeleteSql(table)), modelClass);
            } else {
                sqlSource = new RawSqlSource(configuration, String.format(sqlMethod.getSql(), sqlSelectColumns(table, false), table.getTableName(),
                    table.getKeyColumn(), table.getKeyProperty(), getLogicDeleteSql(table)), Object.class);
            }
            this.addSelectMappedStatement(mapperClass, sqlMethod.getMethod(), sqlSource, modelClass, table);
        } else {
            // 正常查询
            super.injectSelectByIdSql(batch, mapperClass, modelClass, table);
        }
    }

    /**
     * <p>
     * 注入更新 SQL 语句
     * </p>
     *
     * @param mapperClass
     * @param modelClass
     * @param table
     */
    @Override
    protected void injectUpdateByIdSql(boolean selective, Class<?> mapperClass, Class<?> modelClass, TableInfo table) {
        if (table.isLogicDelete()) {
            SqlMethod sqlMethod = selective ? SqlMethod.LOGIC_UPDATE_BY_ID : SqlMethod.LOGIC_UPDATE_ALL_COLUMN_BY_ID;
            String sql = String.format(sqlMethod.getSql(), table.getTableName(), sqlSet(selective, table, "et."),
                table.getKeyColumn(),
                "et." + table.getKeyProperty(),
                "<if test="et instanceof java.util.Map">" +
                    "<if test="et.MP_OPTLOCK_VERSION_ORIGINAL!=null">"
                    + "and ${et.MP_OPTLOCK_VERSION_COLUMN}=#{et.MP_OPTLOCK_VERSION_ORIGINAL}"
                    + "</if>"
                    + "</if>" +
                    getLogicDeleteSql(table)
            );
            SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
            this.addUpdateMappedStatement(mapperClass, modelClass, sqlMethod.getMethod(), sqlSource);
        } else {
            super.injectUpdateByIdSql(selective, mapperClass, modelClass, table);
        }
    }

    /**
     * <p>
     * SQL 更新 set 语句
     * </p>
     *
     * @param table 表信息
     * @return sql and 片段
     */
    public String getLogicDeleteSql(TableInfo table) {
        StringBuilder sql = new StringBuilder();
        List<TableFieldInfo> fieldList = table.getFieldList();
        for (TableFieldInfo fieldInfo : fieldList) {
            if (fieldInfo.isLogicDelete()) {
                sql.append(" AND ").append(fieldInfo.getColumn());
                if (StringUtils.isCharSequence(fieldInfo.getPropertyType())) {
                    sql.append("='").append(fieldInfo.getLogicNotDeleteValue()).append("'");
                } else {
                    sql.append("=").append(fieldInfo.getLogicNotDeleteValue());
                }
            }
        }
        return sql.toString();
    }

    /**
     * <p>
     * SQL 更新 set 语句
     * </p>
     *
     * @param table 表信息
     * @return sql set 片段
     */
    protected String sqlLogicSet(TableInfo table) {
        List<TableFieldInfo> fieldList = table.getFieldList();
        StringBuilder set = new StringBuilder("SET ");
        int i = 0;
        for (TableFieldInfo fieldInfo : fieldList) {
            if (fieldInfo.isLogicDelete()) {
                if (++i > 1) {
                    set.append(",");
                }
                set.append(fieldInfo.getColumn()).append("=");
                if (StringUtils.isCharSequence(fieldInfo.getPropertyType())) {
                    set.append("'").append(fieldInfo.getLogicDeleteValue()).append("'");
                } else {
                    set.append(fieldInfo.getLogicDeleteValue());
                }
            }
        }
        return set.toString();
    }

    // ------------ 处理逻辑删除条件过滤 ------------

    @Override
    protected String sqlWhere(TableInfo table) {
        if (table.isLogicDelete()) {
            StringBuilder where = new StringBuilder("
<where>");
            // 过滤逻辑
            List<TableFieldInfo> fieldList = table.getFieldList();
            // EW 逻辑
            if (StringUtils.isNotEmpty(table.getKeyProperty())) {
                where.append("
<if test="ew.").append(table.getKeyProperty()).append("!=null">");
                where.append(" AND ").append(table.getKeyColumn()).append("=#{ew.");
                where.append(table.getKeyProperty()).append("}");
                where.append("</if>");
            }
            for (TableFieldInfo fieldInfo : fieldList) {
                where.append(convertIfTag(fieldInfo, "ew.", false));
                where.append(" AND ").append(this.sqlCondition(fieldInfo.getCondition(),
                    fieldInfo.getColumn(), "ew." + fieldInfo.getEl()));
                where.append(convertIfTag(fieldInfo, true));
            }
            // 过滤逻辑
            where.append("
").append(getLogicDeleteSql(table));
            where.append("
</where>");
            return where.toString();
        }
        // 正常逻辑
        return super.sqlWhere(table);
    }

    @Override
    protected String sqlWhereEntityWrapper(TableInfo table) {
        if (table.isLogicDelete()) {
            StringBuilder where = new StringBuilder(128);
            where.append("
<where>");
            where.append("
<choose><when test="ew!=null">");
            where.append("
<if test="ew.entity!=null">");
            if (StringUtils.isNotEmpty(table.getKeyProperty())) {
                where.append("
<if test="ew.entity.").append(table.getKeyProperty()).append("!=null">");
                where.append(" AND ").append(table.getKeyColumn()).append("=#{ew.entity.");
                where.append(table.getKeyProperty()).append("}");
                where.append("</if>");
            }
            List<TableFieldInfo> fieldList = table.getFieldList();
            for (TableFieldInfo fieldInfo : fieldList) {
                where.append(convertIfTag(fieldInfo, "ew.entity.", false));
                where.append(" AND ").append(this.sqlCondition(fieldInfo.getCondition(),
                    fieldInfo.getColumn(), "ew.entity." + fieldInfo.getEl()));
                where.append(convertIfTag(fieldInfo, true));
            }
            where.append("
</if>");
            where.append("
").append(getLogicDeleteSql(table));
            where.append("
<if test="ew.sqlSegment!=null">${ew.sqlSegment}
</if>");
            where.append("
</when><otherwise>");
            where.append("
").append(getLogicDeleteSql(table));
            where.append("
</otherwise></choose>");
            where.append("
</where>");
            return where.toString();
        }
        // 正常逻辑
        return super.sqlWhereEntityWrapper(table);
    }

    @Override
    protected String sqlWhereByMap(TableInfo table) {
        if (table.isLogicDelete()) {
            StringBuilder where = new StringBuilder();
            where.append("
<where>");
            // MAP 逻辑
            where.append("
<if test="cm!=null and !cm.isEmpty">");
            where.append("
<foreach collection="cm.keys" item="k" separator="AND">");
            where.append("
<if test="cm[k] != null">");
            where.append(SqlReservedWords.convert(getGlobalConfig(), "
${k}")).append(" = #{cm[${k}]}");
            where.append("</if>");
            where.append("
</foreach>");
            where.append("
</if>");
            // 过滤逻辑
            where.append("
").append(getLogicDeleteSql(table));
            where.append("
</where>");
            return where.toString();
        }
        // 正常逻辑
        return super.sqlWhereByMap(table);
    }

}
View Code

  可以看到这里重写了AutoSqlInjector 的delete 相关方法,如果逻辑删除关闭的话,直接调用父类注入实际删除的逻辑;如果是开启的话则改为update 语句,并且调用 sqlLogicSet 从TableInfo 中获取逻辑删除的字段以及相关的值。

2. 加入一些自己定义的全局SQL

  上面研究了Mybatisplus 简单的加入sql的过程,那么如果我们想注入一些自己的全局的默认的sql如何操作。我们假设全局增加一个根据 全局唯一码操作的方法。

1. 定义全局Mapper

package com.mapper;

import java.util.Collection;
import java.util.List;

import org.apache.ibatis.annotations.Param;

import com.baomidou.mybatisplus.mapper.BaseMapper;

public interface MyBaseMapper<T> extends BaseMapper<T> {

    T selectByUniqueCode(String uniqueCode);

    List<T> selectBatchUniqueCodes(@Param("coll") Collection<String> uniqueCode);

    int deleteByUniqueCode(String uniqueCode);

    int deleteBatchUniqueCodes(@Param("coll") Collection<String> uniqueCode);
}

2. 重写SqlInjector, 这里选择继承AutoSqlInjector:

package com.utils.mybatis;

import org.apache.ibatis.builder.MapperBuilderAssistant;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.scripting.defaults.RawSqlSource;

import com.baomidou.mybatisplus.entity.TableInfo;
import com.baomidou.mybatisplus.mapper.AutoSqlInjector;

public class MyAutoSqlInjector extends AutoSqlInjector {

    @Override
    protected void injectSql(MapperBuilderAssistant builderAssistant, Class<?> mapperClass, Class<?> modelClass,
            TableInfo table) {

        super.injectSql(builderAssistant, mapperClass, modelClass, table);

        // 映射根据唯一编号查询
        injectSelectByUniqueCodeSql(false, mapperClass, modelClass, table);
        injectSelectByUniqueCodeSql(true, mapperClass, modelClass, table);
        // 映射根据唯一编号删除
        injectDeleteByUniqueCodeSql(false, mapperClass, modelClass, table);
        injectDeleteByUniqueCodeSql(true, mapperClass, modelClass, table);
    }

    /**
     * <p>
     * 注入查询 SQL 语句
     * </p>
     *
     * @param batch
     *            是否为批量插入
     * @param mapperClass
     * @param modelClass
     * @param table
     */
    protected void injectSelectByUniqueCodeSql(boolean batch, Class<?> mapperClass, Class<?> modelClass,
            TableInfo table) {

        String tableName = table.getTableName();
        String sql = "SELECT * FROM %s WHERE uniqueCode=#{uniqueCode}";
        SqlSource sqlSource;
        if (batch) {
            String batchSql = "<script>SELECT * FROM %s WHERE uniqueCode IN (%s)</script>";
            StringBuilder ids = new StringBuilder();
            ids.append("
<foreach item="item" index="index" collection="coll" separator=",">");
            ids.append("#{item}");
            ids.append("
</foreach>");
            sqlSource = languageDriver.createSqlSource(configuration,
                    String.format(batchSql, tableName, ids.toString()), modelClass);
            this.addSelectMappedStatement(mapperClass, "selectBatchUniqueCodes", sqlSource, modelClass, table);
        } else {
            sqlSource = new RawSqlSource(configuration, String.format(sql, table.getTableName()), Object.class);
            this.addSelectMappedStatement(mapperClass, "selectByUniqueCode", sqlSource, modelClass, table);
        }
    }

    /**
     * <p>
     * 注入删除 SQL 语句
     * </p>
     *
     * @param mapperClass
     * @param modelClass
     * @param table
     */
    protected void injectDeleteByUniqueCodeSql(boolean batch, Class<?> mapperClass, Class<?> modelClass,
            TableInfo table) {

        String sql = "<script>DELETE FROM %s WHERE uniqueCode=#{%s}</script>";
        String column = "uniqueCode";
        String sqlMethod = "deleteByUniqueCode";
        if (batch) {
            sql = "<script>DELETE FROM %s WHERE uniqueCode IN (%s)</script>";
            StringBuilder ids = new StringBuilder();
            ids.append("
<foreach item="item" index="index" collection="coll" separator=",">");
            ids.append("#{item}");
            ids.append("
</foreach>");
            column = ids.toString();
            sqlMethod = "deleteBatchUniqueCodes";
        }
        String sqlFormated = String.format(sql, table.getTableName(), column);
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sqlFormated, modelClass);
        this.addDeleteMappedStatement(mapperClass, sqlMethod, sqlSource);
    }
}

   如果开启逻辑删除,这里需要继承逻辑删除LogicSqlInjector。这里就模拟AutoSqlInjector 进行注入即可。

  这里也是重写了com.baomidou.mybatisplus.mapper.AutoSqlInjector#injectSql 方法 , 也可以重写com.baomidou.mybatisplus.mapper.AutoSqlInjector#inject(org.apache.ibatis.session.Configuration, org.apache.ibatis.builder.MapperBuilderAssistant, java.lang.Class<?>, java.lang.Class<?>, com.baomidou.mybatisplus.entity.TableInfo)。 因为父类AutoSqlInjector 的 injectSql 方法执行完最后一步会调用inject 方法,可以说为子类添加sql注入暴露了钩子。

3. 我们的Mapper 继承我们上面的MyBaseMapper

package com.mapper.user;

import com.zd.bx.bean.user.Department;
import com.zd.bx.bean.user.User;
import com.zd.bx.mapper.MyBaseMapper;
import com.zd.bx.vo.department.DepartmentVo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

import java.util.List;

@Mapper
public interface DepartmentMapper extends MyBaseMapper<Department> {

    /**
     * 查询部门树
     * 
     * @param parentUniqueCode
     *            父节点唯一编码
     * @return
     */
    List<DepartmentVo> selectByParentId(@Param("parentUniqueCode") String parentUniqueCode);

    /**
     * 查询部门下的人员
     * 
     * @param unCodes
     *            部门唯一编码list
     * @return
     */
    List<User> selectUserByList(List<String> unCodes);

    /**
     * 查询所有子部门的uniqueCode
     *
     * @param parentUniqueCode
     *         父节点uniqueCode
     *
     * @return
     */
    List<String> selectChildrenUnique(@Param("parentUniqueCode") String parentUniqueCode);
}

4. 接下来就是使我们的SqlInjector 生效

(1) 方法一: 直接注入到Spring 中,Spring 启动会扫描

(2) 方法二: 注入MybatisSqlSessionFactoryBean 的过程中设置,一般建议这种。因为我们一般会选择重写这个FactoryBean, 加入一些自己的配置。这个FactoryBean 生产的bean是Mybatis 的SqlSqssionFactory(也就是上面自动注入的对象)

    @Bean
    public MybatisSqlSessionFactoryBean sqlSessionFactoryBean() throws Exception {
        MybatisSqlSessionFactoryBean sessionFactory = new MybatisSqlSessionFactoryBean();
        /**
         * 重点,使分页插件生效
         */
        // 配置数据源,此处配置为关键配置,如果没有将 dynamicDataSource作为数据源则不能实现切换
        sessionFactory.setDataSource(dynamicDataSource());
        // 扫描Model
        sessionFactory.setTypeAliasesPackage("com.zd.bx.bean");
        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        // 扫描映射文件
        sessionFactory.setMapperLocations(resolver.getResources("classpath*:mapper/**/*Mapper.xml"));

        GlobalConfiguration defaults = GlobalConfigUtils.defaults();
        // 设置下划线为false
        defaults.setDbColumnUnderline(false);
        // 设置自定义SQL注入器
        defaults.setSqlInjector(new MyAutoSqlInjector());
        sessionFactory.setGlobalConfig(defaults);


        // 添加插件
        Interceptor[] interceptors = getPlugins();
        if (ArrayUtils.isNotEmpty(interceptors)) {
            sessionFactory.setPlugins(interceptors);
        }

        return sessionFactory;
    }

  这种注入方式和com.baomidou.mybatisplus.spring.boot.starter.MybatisPlusAutoConfiguration#sqlSessionFactory 注入的区别是: 这种方式相当于注入的是个FactoryBean,Spring 启动会过程中会调用其getObject 方法获取生产的bean; 而上面自动注入相当于自己调用了其getObject 方法将生产的对象注入到Spring中。

补充:Mybatisplus有一些比较好的反射工具类 

主要的工具类位于包com.baomidou.mybatisplus.toolkit。其中比较重要的有:

1. ReflectionKit 反射工具类,可以获取相关字段,方法名等。

2. StringUtils 字符串工具栏,有驼峰转下划线、下划线转驼峰等方法。

【当你用心写完每一篇博客之后,你会发现它比你用代码实现功能更有成就感!】
原文地址:https://www.cnblogs.com/qlqwjy/p/15408733.html