菠菜源码-搭建MyBatis的动态SQL解析实现原理概述

MyBati s 菠菜源码-搭建Q2152876294 论坛:diguaym.com中有提供AbstractSQL来构建执行语句,其包括基本的SQLStatement【DELETE,INSERT,SELECT,UPDATE】类型和其对应的SQL语句构建和包装,例如 deleteSQL(builder),insertSQL(builder),selectSQL(builder),updateSQL(builder)等。在SqlRunner中有提供其对应的SQL执行方法,其底层是通过JDBC来实现的,对于Parameters参数的设置或者结果集ResultSet的获取是通过TypeHandler来巧妙实现的【在哪里会使用到?】。

Myabatis解析过程中的每个select,insert,update,delete标签都是MappedStatement对象,里面保存了SqlSource对象的引用。MyBatis的SQL分为静态(Raw)SQL和动态SQL。静态SQL的解析就是替换SQL文本中的#{}参数成?,即生成最终可以预编译的SQL,并把参数相关信息保存到ParameterMapping中,其中包括参数名,数据类型,以及根据数据类型获取对应的TypeHandler【在执行预编译SQL的时候设置参数值,决定参数设置方式】。动态SQL的解析是在执行DB操作的时候调用MappedStatement方法的getBoundSql方式时进行解析的。

静态sql是在应用启动的时候就解析,而动态SQL是在执行该SQL相关操作的时候才根据传入的参数来进行解析的,所以静态SQL效率会比动态SQL高。MyBatis的动态SQL是基于OGNL表达式的,主要元素有IF,WHERE,CASE,FOREACH等。OGNL表达式的实现是通过XMLScriptBuilder的内部接口NodeHandler来实现的,其NodeHandler的实现如下所示:

NodeHandler nodeHandlers(String nodeName) {

Map<String, NodeHandler> map = new HashMap<String, NodeHandler>();

map.put("trim", new TrimHandler());

map.put("where", new WhereHandler());

map.put("set", new SetHandler());

map.put("foreach", new ForEachHandler());

map.put("if", new IfHandler());

map.put("choose", new ChooseHandler());

map.put("when", new IfHandler());

map.put("otherwise", new OtherwiseHandler());

map.put("bind", new BindHandler());

return map.get(nodeName);

}

Mapper的解析涉及到的类是XMLMapperBuilder,其内部使用XMLStatementBuilder来处理XML中的每个INSERT,DELETE,UPDATE和SELECT节点,其内部会使用XMLScriptBuilder来处理节点的SQL部分,最终将遍历产生的结果保存到Configuration的mappedStatements中。针对动态SQL的处理,除了提供NodeHandler外,还有提供很多XXXSqlNode来帮助处理对应的SQL节点,通常用于SQL节点(choose|foreach|if|)中,实现apply方法即可。

在scripting包中存在LanguageDriver【通常是XMLLanguageDriver实现】来创建SqlSource和ParameterHandler。SqlSource的实现类分别是有DynamicSqlSource,RawSqlSource和StaticSqlSource,获取到的SqlSource最终会保存到配置中,然后通过Configuration可以得到BoundSQL【将动态内容都处理完成后得到的SQL语句字符串,其中包括?和绑定的参数】。

总结:MyBatis的SQL解析实现原理并不复杂,但是底层源码的实现极其复杂。从宏观上来看就是解析相关的XML文件,获取其对应的内容,然后保存到Configuration类中,最终从Configuration类中获取对应的对象。

猜你喜欢

转载自blog.51cto.com/13955271/2170476