SQL模板注册

SQL模板注册,就是创建和添加MappedStatement

mybatis-plus会复用mybatis,将xxxMapper.xml文件定义的sql模板解析成MappedStatement。因此在xxxMapper.xml定义sql用法和mybatis一样

同时,mybatis-plus会执行自己的的方法,将预定义的selectList,insert,update,delete等方法,通过生成mybatis脚本的方式转成MappedStatement。

mybatis-plus的Wrapper是mybatis的实体,mybatis会反射解析属性,对脚本动态内容进行判断和替换

脚本代码会转成sqlNode对象,脚本动态内容进行判断和替换的过程是转成sqlNode后执行的

<script>
    <if test="ew != null and ew.sqlFirst != null">
        ${ew.sqlFirst}
    </if> 

    SELECT 

    <choose>
        <when test="ew != null and ew.sqlSelect != null">
            ${ew.sqlSelect}
        </when>
        <otherwise>
            trade_id,created_at,updated_at
        </otherwise>
    </choose> 

    FROM trade 

    <if test="ew != null">
        <where>
            <if test="ew.entity != null">
                <if test="ew.entity['tradeId'] != null"> 
                    AND trade_id=#{ew.entity.tradeId}
                </if>
                <if test="ew.entity['createdAt'] != null"> 
                    AND created_at=#{ew.entity.createdAt}
                </if>
                <if test="ew.entity['updatedAt'] != null"> 
                    AND updated_at=#{ew.entity.updatedAt}
                </if>
            </if>

            <if test="ew.sqlSegment != null and ew.sqlSegment != '' and ew.nonEmptyOfWhere">
                <if test="ew.nonEmptyOfEntity and ew.nonEmptyOfNormal"> 
                    AND
                </if> 
                ${ew.sqlSegment}
            </if>
        </where>

        <if test="ew.sqlSegment != null and ew.sqlSegment != '' and ew.emptyOfWhere">
            ${ew.sqlSegment}
        </if>
    </if>  

    <if test="ew != null and ew.sqlComment != null">
        ${ew.sqlComment}
    </if>
</script>

MybatisPlusAutoConfiguration

MybatisPlusAutoConfiguration的sqlSessionFactory()方法注册SqlSessionFactory类型的bean。是由MybatisSqlSessionFactoryBean进行处理的

public class MybatisPlusAutoConfiguration implements InitializingBean {
    @Bean
    @ConditionalOnMissingBean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        MybatisSqlSessionFactoryBean factory = new MybatisSqlSessionFactoryBean();
        return factory.getObject();
    }
}

MybatisSqlSessionFactoryBean

MybatisSqlSessionFactoryBean 实际上构建sqlSessionFactory是通过afterPropertiesSet方法

public class MybatisSqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
    public SqlSessionFactory getObject() throws Exception {
        if (this.sqlSessionFactory == null) {
            this.afterPropertiesSet();
        }

        return this.sqlSessionFactory;
    }

    public void afterPropertiesSet() throws Exception {
        Assert.notNull(this.dataSource, "Property 'dataSource' is required");
        Assert.state(this.configuration == null && this.configLocation == null || this.configuration == null || this.configLocation == null, "Property 'configuration' and 'configLocation' can not specified with together");
        SqlRunner.DEFAULT.close();
        this.sqlSessionFactory = this.buildSqlSessionFactory();
    }

    protected SqlSessionFactory buildSqlSessionFactory() throws Exception {
        // xml文件解析
        XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(), (Configuration)targetConfiguration, mapperLocation.toString(), ((Configuration)targetConfiguration).getSqlFragments());
        xmlMapperBuilder.parse();

        //
    }


}

XMLMapperBuilder

XMLMapperBuilder解析TradeMapper.xml,并将文件内的sql模板语句注册成MappedStatement

public class XMLMapperBuilder extends BaseBuilder {
    public void parse() {
        if (!configuration.isResourceLoaded(resource)) {
            configurationElement(parser.evalNode("/mapper"));
            configuration.addLoadedResource(resource);
            bindMapperForNamespace();
        }
    }

    private void configurationElement(XNode context) {
        try {
            buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
        } catch (Exception e) {
            throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
        }
    }

    private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
        for (XNode context : list) {
            final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
            try {
                statementParser.parseStatementNode();
            } catch (IncompleteElementException e) {
                configuration.addIncompleteStatement(statementParser);
            }
        }
    }

    public void parseStatementNode() {
        builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
                fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
                resultSetTypeEnum, flushCache, useCache, resultOrdered,
                keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
    }
}

MybatisConfiguration

XMLMapperBuilder 通过configuration,转到了MybatisConfiguration.addMapper() 方法

public class XMLMapperBuilder extends BaseBuilder {
    private void bindMapperForNamespace() {
        configuration.addMapper(boundType);
    }
}

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

MybatisMapperRegistry

public class MybatisMapperRegistry extends MapperRegistry {
    @Override
    public <T> void addMapper(Class<T> type) {
        if (type.isInterface()) {
            boolean loadCompleted = false;
            try {
                // TODO 这里也换成 MybatisMapperProxyFactory 而不是 MapperProxyFactory
                knownMappers.put(type, new MybatisMapperProxyFactory<>(type));
                // TODO 这里也换成 MybatisMapperAnnotationBuilder 而不是 MapperAnnotationBuilder
                MybatisMapperAnnotationBuilder parser = new MybatisMapperAnnotationBuilder(config, type);
                parser.parse();
                loadCompleted = true;
            } finally {
                if (!loadCompleted) {
                    knownMappers.remove(type);
                }
            }
        }
    }
}

MybatisMapperAnnotationBuilder

public class MybatisMapperAnnotationBuilder extends MapperAnnotationBuilder {
    @Override
    public void parse() {
        String resource = type.toString();
        if (!configuration.isResourceLoaded(resource)) {
            try {
                // https://github.com/baomidou/mybatis-plus/issues/3038
                if (GlobalConfigUtils.isSupperMapperChildren(configuration, type)) {
                    parserInjector();
                }
            } catch (IncompleteElementException e) {
                configuration.addIncompleteMethod(new InjectorResolver(this));
            }
        }
        parsePendingMethods();
    }

    void parserInjector() {
        GlobalConfigUtils.getSqlInjector(configuration).inspectInject(assistant, type);
    }

}

AbstractSqlInjector

public abstract class AbstractSqlInjector implements ISqlInjector {
    @Override
    public void inspectInject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass) {
        // 循环注入自定义方法
        methodList.forEach(m -> m.inject(builderAssistant, mapperClass, modelClass, tableInfo));
    }
}

AbstractMethod

public abstract class AbstractMethod implements Constants {
    public void inject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
        injectMappedStatement(mapperClass, modelClass, tableInfo);
    }
}

SelectList

public class SelectList extends AbstractMethod {
    @Override
    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
        SqlMethod sqlMethod = SqlMethod.SELECT_LIST;
        String sql = String.format(sqlMethod.getSql(), sqlFirst(), sqlSelectColumns(tableInfo, true), tableInfo.getTableName(),
                sqlWhereEntityWrapper(true, tableInfo), sqlOrderBy(tableInfo), sqlComment());
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
        return this.addSelectMappedStatementForTable(mapperClass, getMethod(sqlMethod), sqlSource, tableInfo);
    }
}