二手房发布网站怎么做,免费开店平台,精品网站制作公司,wordpress文章保存解析语句标签 Select|Update|Insert|Delete 一、前言二、语句标签的源码分析三、sql 标签的解析四、总结 一、前言
在阐述解析语句标签之前#xff0c;得先知道我们的语句标签内容最后被封装到Configuration哪#xff1f;#xff08;都应该知道 Mybatis 通过的是 XMLConfig… 解析语句标签 Select|Update|Insert|Delete 一、前言二、语句标签的源码分析三、sql 标签的解析四、总结 一、前言
在阐述解析语句标签之前得先知道我们的语句标签内容最后被封装到Configuration哪都应该知道 Mybatis 通过的是 XMLConfigBuilder 去解析 xml 然后封装到Configuration对象中传递给 SqlSessionFactory 去往下执行。
而语句标签的解析内容即被封装到了 Configuration 中的 mappedStatements Map对象中即如下属性
protected final MapString, MappedStatement mappedStatements new StrictMapMappedStatement(Mapped Statements collection).conflictMessageProducer((savedValue, targetValue) - . please check savedValue.getResource() and targetValue.getResource());其 key 对应的是标签 id而 MappedStatement 是对语句标签内容的封装实例类它是一个 final 类最终是通过内部的 Builder 类 build 构建出来的未向外提供构造你可以理解为一条 Select|Update|Insert|Delete 语句标签映射着一个 MappedStatement就如同一个ResultMap 字段对应着一个 ResultMapping 一样。内部属性如下 private String resource;// 源自于哪个mapper文件private Configuration configuration;// 配置文件private String id;// idprivate Integer fetchSize;// 每次从服务器获取的数量private Integer timeout;// 最长访问数据库的时间private StatementType statementType;// STATEMENT、PREPARED、CALLABLEprivate ResultSetType resultSetType;private SqlSource sqlSource;// 对SQL的包装private Cache cache;// 缓存private ParameterMap parameterMap;private ListResultMap resultMaps;// 结果映射private boolean flushCacheRequired;// 是否需要缓存刷新private boolean useCache;// 是否使用二级缓存private boolean resultOrdered;private SqlCommandType sqlCommandType;// UNKNOWN、INSERT、UPDATE、SELECTprivate KeyGenerator keyGenerator;// 主键生成器private String[] keyProperties;private String[] keyColumns;private boolean hasNestedResultMaps;// 是否存在嵌套查询结果集private String databaseId;private Log statementLog;private LanguageDriver lang;// 语言驱动永磊解析动态或静态SQLprivate String[] resultSets;// resultsetprivate boolean dirtySelect;很多属性都是大伙熟知的像 SqlSource、LanguageDriver 在我之前的博客中也有对其的源码解析后者是对前者的获取前者是获取最终要执行的 sql 的封装boundsql。【Mybatis源码分析】动态标签的底层原理DynamicSqlSource源码分析
大概流程就如下图这样
前言先到这开始真正的源码分析。
二、语句标签的源码分析
这边的话由于是博客不会从头到尾对源码进行分析只对核心部分进行解析在进行之前先对 Mybatis 对 xml 解析时使用到的构造器总览构个图了解了整体对源码分析更有帮助自身观看源码后得出来的如有问题评论区可以指出。图虽然直观但想出来感觉太酷了也不知道如果我来设计的话要吃几个核桃才能设计出来 解析语句标签的核心内容在 XMLStatementBuilder.parseStatementNode 方法中解析完标签内容然后将其通过 MapperBuilderAssistant 对象映射成 MapperStatement 然后封装到 Configuration 中的 mappedStatements 中。
核心源码如下-有删减可以大致看看细节或者说主要部分下面有图片详细解释
public void parseStatementNode() {String id context.getStringAttribute(id);String nodeName context.getNode().getNodeName();SqlCommandType sqlCommandType SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));boolean isSelect sqlCommandType SqlCommandType.SELECT;boolean flushCache context.getBooleanAttribute(flushCache, !isSelect);boolean useCache context.getBooleanAttribute(useCache, isSelect);boolean resultOrdered context.getBooleanAttribute(resultOrdered, false);// Include Fragments before parsing// 这是对 sql 片段的引用在下面对 sql 标签进行了源码分析// 这里是通过 include 标签对 sql 标签内容的引用// 可以说是替换内容吧XMLIncludeTransformer includeParser new XMLIncludeTransformer(configuration, builderAssistant);includeParser.applyIncludes(context.getNode());String parameterType context.getStringAttribute(parameterType);Class? parameterTypeClass resolveClass(parameterType);// 这里的话可以自定义 LanguageDriver 对象然后去使用然后获取对应的 SqlSource对象// 这里的话默认是 XMLLanguageDriverString lang context.getStringAttribute(lang);LanguageDriver langDriver getLanguageDriver(lang);// Parse selectKey after includes and remove them.processSelectKeyNodes(id, parameterTypeClass, langDriver);// Parse the SQL (pre: selectKey and include were parsed and removed)KeyGenerator keyGenerator;String keyStatementId id SelectKeyGenerator.SELECT_KEY_SUFFIX;keyStatementId builderAssistant.applyCurrentNamespace(keyStatementId, true);if (configuration.hasKeyGenerator(keyStatementId)) {keyGenerator configuration.getKeyGenerator(keyStatementId);} else {keyGenerator context.getBooleanAttribute(useGeneratedKeys,configuration.isUseGeneratedKeys() SqlCommandType.INSERT.equals(sqlCommandType))? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;}SqlSource sqlSource langDriver.createSqlSource(configuration, context, parameterTypeClass);// 去得到最后执行语句准备使用的语句类型默认是 PreparedStatementStatementType statementType StatementType.valueOf(context.getStringAttribute(statementType, StatementType.PREPARED.toString()));String parameterMap context.getStringAttribute(parameterMap);// 获取返回值类型String resultType context.getStringAttribute(resultType);Class? resultTypeClass resolveClass(resultType);String resultMap context.getStringAttribute(resultMap);if (resultTypeClass null resultMap null) {resultTypeClass MapperAnnotationBuilder.getMethodReturnType(builderAssistant.getCurrentNamespace(), id);}// 通过助手去构建 MappedStatement// 并且封装进 configuration 中的 mappedStatements中builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap,parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, resultOrdered,keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets, dirtySelect);}测试的 xml sql idperson_testid,name,age,sex/sqlselect idhhh resultTypecom.ncpowernode.mybatis.bean.Personselect include refidperson_test/from t_person/select下面我认为构建过程中几个主要的属性解析特地说明一下
你没配置 lang 属性的话默认就是通过 XMLLanguageDriver 进行解析的。
按照测试的语句的话我是没用使用动态SQL的所以SQLSource解析结果应该是RawSQLSource对象。 没有去指定对应的 StatementType 语句类型的话默认就是使用 PreparedStatement。 如果是 Select 语句标签的话需要指定 resultType 属性或 resultMap 的原因都没指定的话就把 namespace 指定的类当做返回对象 而后就通过 Mapper 助手去构建 MappedStatement 对象并且映射咯。 即通过 MappedStatement.Builder 进行构建然后封装到 configuration 中。 封装的话就直接 put(全限定idMappedStatement对象咯 三、sql 标签的解析
测试代码如下 sql idperson_testid,name,age,sex/sqlselect idhhh resultTypecom.ncpowernode.mybatis.bean.Personselect include refidperson_test/from t_person/select Testpublic void testSqlTag(){SqlSessionFactoryBuilder builder new SqlSessionFactoryBuilder();SqlSessionFactory sqlSessionFactory builder.build(Thread.currentThread().getContextClassLoader().getResourceAsStream(mybatis-config.xml));SqlSession sqlSession sqlSessionFactory.openSession();PersonMapper mapper sqlSession.getMapper(PersonMapper.class);ListPerson xx mapper.hhh();}核心解析源码如下本质就是将 sql 片段的内容封装到 sqlFragments 这个 map 中然后后续供语句标签里使用 private void sqlElement(ListXNode list, String requiredDatabaseId) {for (XNode context : list) {String databaseId context.getStringAttribute(databaseId);String id context.getStringAttribute(id);// 合并成全idnamespace.id 这种形式全限定idid builderAssistant.applyCurrentNamespace(id, false);if (databaseIdMatchesCurrent(id, databaseId, requiredDatabaseId)) {sqlFragments.put(id, context);}}}四、总结
其实通过这些源码分析也知道了为什么默认是使用 XMLLanguageDriver 去获取 SQLSource为什么默认是使用 PreparedStatement 去操纵最后的 SQL查询Select语句中不指定resultType 、resultMap 属性行不行行但默认就是返回你 namespace 指定的类。我们可以通过配置标签的 lang 属性来指定对应的 LanguageDriver 实现类去获取 SqlSource.当然我觉得默认的已经很棒了这个应该用不着自己写。在 mapper 文件中使用了 sql 标签可以使用 include 标签去使用 sql 标签中的内容。
下面我画的俩图感觉还挺形象 这里说点题外话在 XMLScriptBuilder 中解析动态标签时${} 也是被解析成动态sql对应的动态 SQLNode 是 TextSqlNode内部 apply 方法是通过 GenericTokenParser 解析完然后封装到 DynamicContext 中的。而解析 #{} 时是不被算作为动态 sql 的这是因为不管是 RawSqlSource 还是 DynamicSqlSource都会通过 SqlSourceBuilder.parse 方法对 #{} 进行处理。在上次博客中我自己也是理解的模模糊糊这里明确后所以再指明一下