Java训练Mybatis动态Sql处理分析(wb数据怎么处理分析)

下面的文章来自动态Sql架构师必备简介动态Sql是MyBatis的强大特性之一。如果您使用过JDBC或其他类似的框架,您应该理解根据不同的条件连接SQL语句是多么痛苦。例如,在连接时,确保不要忘记

以下文章来自Architect Must-Have

动态Sql简介

动态SQL是MyBatis的强大特性之一。如果您使用过JDBC或其他类似的框架,您应该理解根据不同的条件连接SQL语句是多么痛苦。例如,在连接时,确保不要忘记添加必要的空格,还要注意删除列表最后一个列名中的逗号。使用动态SQL,您可以完全摆脱这种痛苦。

使用动态SQL并不是一件容易的事情,但是MyBatis通过可以在任何SQL映射语句中使用的强大的动态SQL语言,显著地提高了该特性的易用性。

在Mybatis动态分析中有两个核心类SqlNode、SqlSource和ExpressionEvaluator。Mybatis动态Sql的使用分为两个部分:动态Sql解析和动态Sql拼接执行。

包SqlNode

SqlNode在解析Xml文件时解析动态Sql,并将其存储在MappedStatement的sqlSource属性中。对于嵌套动态Sql, mybatis使用递归调用进行解析。就我个人而言,我认为这个东西还是比较复杂的,所以这个博主准备了示例、源代码和执行结果来一起解释_java训练。

Sql脚本分类

Mybatis中的Sql脚本分为两种类型:静态Sql和动态Sql。让’s通过具体的源代码来看看两者之间的区别。

静态Sql和动态Sql

说白了,没有哪一个Sql脚本过于主观而难以理解。

// Select是查询的某个属性

& lt;选择id = & # 8221; selectBypageTwo& # 8221;resultType = & # 8221; com.wwl.mybatis.dao.User& # 8221;比;

//查询语句select * from user where id >#{用户。id}是Mybatis中的静态Sql

//静态Sql是不带任何条件的Sql语句

Select * from user where id ># {user.id}

//这里有if判断条件,Mybatis调用带有判断条件的Sql动态Sql。

//除了if, Dynamic Sql还有foreach, where, trim等。详情请访问mybatis官方网站。

<if test=”user.name != null和user.name!=””>

AND name = #{user.name}

&lt / if>

& lt; / select>

SqlNode类结果系统

在查看mybatis代码时,经常可以看到这种结构。每个SqlNode负责自己的函数。单一的责任。SqlNode的核心应用方法是通过ExpressionEvaluator解析OGNL表达式数据。接下来,让’s看看Mybatis如何递归解析动态sql脚本。

//解析Sql脚本节点

public SqlSource parseScriptNode( {

//解析静态和动态脚本并将其存储在MixedSqlNode中

//这行代码非常关键,我们将在后面分析parseDynamicTags,这里是递归地一层一层调用这个方法,从Sql脚本生成一个MixedSqlNode对象。

MixedSqlNode rootSqlNode = parseDynamicTags(context);

SqlSource sqlSource = null;

//是否是动态Sql

如果

//动态Sql生成DynamicSqlSource

sqlSource = new DynamicSqlSource(configuration, rootSqlNode);

} else {

//否则为静态SqlSource

sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);

}

返回sqlSource;

}

//一个突出显示的块

protected MixedSqlNode parseDynamicTags(XNode节点){

//创建一个SqlNode,这个列表存储当前Sql脚本节点下的所有SqlNode信息

List< SqlNode>contents = new ArrayList<SqlNode>();

NodeList子节点= node.getNode().getChildNodes();

For (int I = 0;我& lt;children.getLength ();我+ +){

XNode子节点= node.newXNode(children.item(i));

//确定子元素中的文本内容或子元素文档中的属性|| CDATA部分(解析器不会解析的文本)

if (child.getNode(). getnodetype() ==节点。CDATA_SECTION_NODE || child.getNode().getNodeType() == Node.TEXT_NODE) {

字符串数据= child.getStringBody(“”);

/ /解析数据

TextSqlNode = new TextSqlNode(data);

//判断当前Sql脚本是否是动态脚本

if (textSqlNode.isDynamic()) {

contents.add (textSqlNode);

isDynamic = true;

} else {

content.add(new StaticTextSqlNode(data;

}

//如果子元素是代表性元素,则需要解析子元素

} else if (child.getNode().getNodeType() == Node.ELEMENT_NODE) {// issue #628

//获取元素的名称

String nodeName = child.getNode().getNodeName();

//根据元素名称获取元素节点的处理器,Mybatis提供8个元素处理器,ChooseHandler, IfHandler, OtherwiseHandler

//TrimHandler, BindHandler, WhereHandler, SetHandler, ForEachHandler。博主会为你分析IfHandler

NodeHandler handler = nodeHandlerMap.get(nodeName;

If (handler == null) {

抛出新的BuilderException(“未知元素<”+ nodeName + “>在SQL语句.”);

}

//调用相应的节点处理程序,递归调用在这里

处理程序。handleNode(孩子、内容);

isDynamic = true;

}

}

/ /创建MixedSqlNode

返回新的MixedSqlNode(contents);

}

//让’s看看IfHandler如何处理它。IfHandler是XMLScriptBuilder的一个内部类

私有类IfHandler实现NodeHandler {

公共IfHandler() {

//防止合成访问

}

//我们专注于分析这个方法

@Override

public void handleNode(XNode nodeToHandle, List<targetContents) {

//调用parseDynamicTags进行节点解析。这里是递归,上面的方法再次被调用。

MixedSqlNode = parseDynamicTags(nodeToHandle);

//获取if对应的表达式

字符串测试= nodeToHandle.getStringAttribute(“test”);

/ /创建IfSqlNode

IfSqlNode = new IfSqlNode(mixedSqlNode, test);

targetContents.add ifSqlNode);

}

}

让’s基于Sql脚本和执行结果分析它。

//静态Sql脚本和嵌套动态Sql脚本

& lt;选择id = & # 8221; selectBypageTwo& # 8221;resultType = & # 8221; com.wwl.mybatis.dao.User& # 8221;比;

Select * from user where id ># {user.id}

<if test=”user.name != null和user.name!=””>

AND name = #{user.name}

<if test=”user.name != null和user.name!=””>

AND name = #{user.name}

<if test=”user.name != null和user.name!=””>

AND name = #{user.name}

&lt / if>

&lt / if>

&lt / if>

& lt; / select>

让’s分析执行结果:

上面的递归结果用了不合理的颜色标记,你可以自己看。特别是,您需要查看IfSqlNode的属性。

动态Sql分析

动态Sql解析主要是将动态Sql转换为JDBC在执行数据库操作时可以识别的Sql脚本。在Mybatis中,Sql脚本主要通过SqlSource解析,并替换为JDBC可以识别的Sql脚本。让我们首先看一下类图。

SqlSource:提供Sql解析的行为。
编译静态Sql脚本,只生成StaticSqlSource一次。
DynamicSqlSource:为每个调用生成StaticSqlSource。每次调用中传递的参数可能不同。每次都需要生成StaticSqlSource。
ProviderSqlSource:第三方脚本语言的集成。
FreeMarkerSqlSource:支持FreeMarker。
StaticSqlSource: StaticSqlSource只封装上述4种类型。没有这个类,博主们会更精神。
这次我们主要分析StaticSqlSource、RawSqlSource和DynamicSqlSource。

StaticSqlSource

实际上,StaticSqlSource是打包其他几种类型的Sql处理器的结果。让’s查看源代码。

//我们主要分析getBoundSql

公共类StaticSqlSource实现了SqlSource {

sql语句;

private final List<parameterMappings;

私有最终配置配置;

public StaticSqlSource(配置配置,字符串sql) {

This (configuration, sql, null);

}

public StaticSqlSource(Configuration Configuration, String sql, List<ParameterMapping>parameterMappings) {

这一点。SQL = SQL;

这一点。parameterMappings = parameterMappings;

这一点。配置=配置;

}

//getBoundSql是创建一个BoundSql对象。

@Override

getBoundSql(对象参数对象){

返回新的BoundSql(configuration, sql, parameterMappings, parameterObject);

}

}

读完之后是不是很简单?事实上,有些代码并不像我们想象的那么难。

RawSqlSource

//我们专注于分析RawSqlSource方法

公共类RawSqlSource实现SqlSource {

private final SqlSource;

public RawSqlSource(Configuration Configuration, SqlNode rootSqlNode, Class<parameterType) {

this(configuration, getSql(configuration, rootSqlNode), parameterType);

}

//这里实现了静态脚本的解析。所谓静态脚本解析就是将#{}解析成?静态Sql解析在解析Mapper.xml时执行

public RawSqlSource(Configuration Configuration, String sql, Class<?>parameterType) {

SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(配置);

Class< ?比;clazz = parameterType == null ?Object.class: parameterType;

//调用SqlSourceBuilder的解析方法来解析Sql

sqlSource = sqlSourceParser。解析(sql, clazz, new HashMap<String, Object>());

}

getSql(Configuration Configuration, SqlNode rootSqlNode) {

DynamicContext context = new DynamicContext(configuration, null);

rootSqlNode.apply(上下文);

返回context.getSql ();

}

@Override

getBoundSql(对象参数对象){

返回sqlSource.getBoundSql (parameterObject);

}

}

让我们看一下SqlSourceBuilder的解析方法

public SqlSource parse(String originalSql, Class<parameterType, Map<String, Object>additionalParameters) {

ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler(configuration, parameterType, addialparameters);

//在Sql脚本中找到#{}符号的脚本?要更换的号码。GenericTokenParser中的代码相对复杂,本文作者还没有研究过它。

//如果你感兴趣,你可以自己学习。

generictokenser =新generictoser(“#{“}”,处理者);

String sql = parser.parse(originalSql);

返回新的StaticSqlSource(configuration, sql, handler.getParameterMappings());

}

DynamicSqlSource

动态Sql分析主要由DynamicSqlSource完成。同样,sql分析是通过递归调用执行的。我们还是用上面的Sql给大家解释一下。

公共类DynamicSqlSource实现SqlSource {

私有最终配置配置;

private final SqlNode;

public DynamicSqlSource(Configuration Configuration, SqlNode rootSqlNode) {

这一点。配置=配置;

这一点。rootSqlNode = rootSqlNode;

}

@Override

getBoundSql(对象参数对象){

//动态Sql解析上下文

动态背景=新动态背景(配置,参数);

//rootSqlNode是我们前面解释的,将动态Sql解析为SqlNode对象。外层是MixedSqlNode节点,它存储

//该节点下的所有子节点。它递归调用并根据传入参数的属性检查是否需要拼接sql

rootSqlNode.apply(上下文);

//此代码与上面的静态Sql连接代码一致。

SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(配置);

Class< ?比;parameterType = parameterObject == null ?Object.class: parameterObject.getClass();

//将动态Sql中的#{}替换为?

SqlSource = sqlSourceParser. getSql()、paraparation type、context.getBindings();

BoundSql = sqlSource.getBoundSql(parameterObject);

for (Map.Entry<String, Object> entry : context.getBindings(.entrySet( {

boundSql.setAdditionalParameter(entry.getKey(, entry.getValue(;

}

返回boundSql;

}

}

动态Sql分析应用方法博主只根据场景介绍了MixedSqlNode和IfSqlNode的应用方法。其他人则对自己的研究感兴趣。逻辑大致相同,但实现略有不同。

公共类MixedSqlNode实现SqlNode {

SqlNode>内容;

公共MixedSqlNode (List< SqlNode>内容){

这一点。内容=内容;

}

//获取循环SqlNode列表中的所有SqlNode,根据传入的参数和条件调用apply方法拼接静态sql。

//列表中的SqlNode可以是一个简单的SqlNode对象,也可以是一个MixedSqlNode或有更多嵌套。

// blog ’s的例子是3个嵌套的If查询。根据blogger’s Sql脚本,在这里将直接调用IfSqlNode的apply方法。

下面让我们看看IfSqlNode是如何实现的。

@Override

公共布尔应用(DynamicContext上下文){

for (SqlNode SqlNode: contents) {

sqlNode.apply(上下文);

}

返回true;

}

}

申请IfSqlNode

公共类IfSqlNode实现SqlNode {

//ExpressionEvaluator将调用ognl来解析表达式

私有final expression;

private final字符串测试;

private final SqlNode内容;

public IfSqlNode(SqlNode内容,字符串测试){

这一点。测试=测试;

这一点。内容=内容;

this.evaluator = new ExpressionEvaluator(;

}

@Override

公共布尔应用(DynamicContext上下文){

//context.getBindings()存储请求参数,这里是一个HashMap, OGNl中的代码博主还没有研究过它。

//如果条件为true,则直接获取内容中SqlNode的应用方法进行动态脚本处理。

如果(评估者。evaluateBoolean(test, context.getBindings())) {

contents.apply(上下文);

返回true;

}

返回错误;

}

}

这段代码有很多递归调用,博客作者认为它不是很彻底,所以您必须在阅读后自己调试它。

总结

Mybatis动态Sql从解析到执行分为两个过程。下面是这两个过程的简要总结。
1. 动态Sql生成SqlNode信息。这个过程发生在解析Sql语句(如select和update)的过程中。如果它是静态Sql,它将直接用?.
2. 获取BoundSql时触发动态Sql解析。将调用SqlNode的应用程序将Sql解析为静态Sql,然后将#{}替换为?,绑定ParameterMapping映射。

JAVA

零代码开发平台–信息系统装配线工厂(零代码开发平台免费版)

2023-1-18 13:15:28

JAVA

IDEA配置Git项目的运行环境(非maven)(idea怎么配置git环境)

2023-1-18 13:22:21

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索