在我们使用mybatis的时候,有没有思考过mybatis中解析xml中动态sql的。
这里可以从mybatis的test中可以看到:xml映射构建测试和xml配置构建测试,这里以xml映射构建为例,来看一下它从解析中可以看到什么。
xml映射构建测试类在mybatis中,我们经常会看到mybatis的xml中的sql带有if、choose…when、where等标签,那它们是怎样被解析的呢?
其追踪过程:
XMLMapperBuilder#parse-XMLMapperBuilder#configurationElement#buildStatementFromContext#parseStatementNode-XMLLanguageDriver#createSqlSource-XMLScriptBuilder#parseDynamicTags-XMLScriptBuilder#handleNode-执行解析动态节点标签if/where/choose等
测试解析入口:
//创建配置对象,获取xml映射构建,通过xml映射构建进行解析
TestvoidshouldSuccessfullyLoadXMLMapperFile()throwsException{Configurationconfiguration=newConfiguration();Stringresource="org/apache/ibatis/builder/AuthorMapper.xml";try(InputStreaminputStream=Resources.getResourceAsStream(resource)){XMLMapperBuilderbuilder=newXMLMapperBuilder(inputStream,configuration,resource,configuration.getSqlFragments());builder.parse();}}进行mapper进行构建,首先通过xpath进行解析:
publicXMLMapperBuilder(InputStreaminputStream,Configurationconfiguration,Stringresource,MapString,XNodesqlFragments){this(newXPathParser(inputStream,true,configuration.getVariables(),newXMLMapperEntityResolver()),configuration,resource,sqlFragments);}
然后通过xml映射构建进行解析:
//执行解析操作publicvoidparse(){//配置中如果不是资源加载if(!configuration.isResourceLoaded(resource)){//配置元素重要configurationElement(parser.evalNode("/mapper"));//将其添加到加载资源中configuration.addLoadedResource(resource);//为命名空间绑定映射bindMapperForNamespace();}//解析等待的结果maps、缓存引用、语句parsePendingResultMaps();parsePendingCacheRefs();parsePendingStatements();}
可以看到xpath的解析的结果是XNode对象:
//配置元素privatevoidconfigurationElement(XNodecontext){try{Stringnamespace=context.getStringAttribute("namespace");if(namespace==null
namespace.isEmpty()){thrownewBuilderException("Mappersnamespacecannotbeempty");}builderAssistant.setCurrentNamespace(namespace);cacheRefElement(context.evalNode("cache-ref"));cacheElement(context.evalNode("cache"));parameterMapElement(context.evalNodes("/mapper/parameterMap"));resultMapElements(context.evalNodes("/mapper/resultMap"));sqlElement(context.evalNodes("/mapper/sql"));//构建语句从上下文中buildStatementFromContext(context.evalNodes("select
insert
update
delete"));}catch(Exceptione){thrownewBuilderException("ErrorparsingMapperXML.TheXMLlocationis"+resource+".Cause:"+e,e);}}
构建语句从上下文,解析语句节点:
//构建语句从上下文privatevoidbuildStatementFromContext(ListXNodelist){if(configuration.getDatabaseId()!=null){buildStatementFromContext(list,configuration.getDatabaseId());}buildStatementFromContext(list,null);}//构建语句从上下文privatevoidbuildStatementFromContext(ListXNodelist,StringrequiredDatabaseId){for(XNodecontext:list){finalXMLStatementBuilderstatementParser=newXMLStatementBuilder(configuration,builderAssistant,context,requiredDatabaseId);try{//解析语句节点statementParser.parseStatementNode();}catch(In