zoukankan      html  css  js  c++  java
  • 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 字符串工具栏,有驼峰转下划线、下划线转驼峰等方法。

    【当你用心写完每一篇博客之后,你会发现它比你用代码实现功能更有成就感!】
  • 相关阅读:
    Web服务器—Nginx
    Web服务器—IIS
    Linux—主机扫描工具(Nmap)
    Linux—系统关机命令详解
    Linux—开机启动过程详解
    Python—版本和环境的管理工具(Pipenv)
    Flask—好的博客
    Mysql—事务原理与详解
    汇编刷题:统计内存中的一个十六位二进制数 位值为1的位个数之和
    汇编刷题:在M单元和N单元分别存有一个8位无符号数36H和95H,要求比较并输出 M大于N 或者 M小于N
  • 原文地址:https://www.cnblogs.com/qlqwjy/p/15408733.html
Copyright © 2011-2022 走看看