Mybatis源码分析:trim标签

*/

.hljs {
display: block;
overflow-x: auto;
padding: 0.5em;
background: #F0F0F0;
}

/* Base color: saturation 0; */

.hljs,
.hljs-subst {
color: #444;
}

.hljs-comment {
color: #888888;
}

.hljs-keyword,
.hljs-attribute,
.hljs-selector-tag,
.hljs-meta-keyword,
.hljs-doctag,
.hljs-name {
font-weight: bold;
}

/* User color: hue: 0 */

.hljs-type,
.hljs-string,
.hljs-number,
.hljs-selector-id,
.hljs-selector-class,
.hljs-quote,
.hljs-template-tag,
.hljs-deletion {
color: #880000;
}

.hljs-title,
.hljs-section {
color: #880000;
font-weight: bold;
}

.hljs-regexp,
.hljs-symbol,
.hljs-variable,
.hljs-template-variable,
.hljs-link,
.hljs-selector-attr,
.hljs-selector-pseudo {
color: #BC6060;
}

/* Language color: hue: 90; */

.hljs-literal {
color: #78A960;
}

.hljs-built_in,
.hljs-bullet,
.hljs-code,
.hljs-addition {
color: #397300;
}

/* Meta color: hue: 200 */

.hljs-meta {
color: #1f7199;
}

.hljs-meta-string {
color: #4d99bf;
}

/* Misc effects */

.hljs-emphasis {
font-style: italic;
}

.hljs-strong {
font-weight: bold;
}
-->

trim标签使用

   标签用于对标签内的sql语句进行前后缀补齐或者前后缀删除。该标签提供了四个属性,prefix,prefixOverrides,suffix,suffixOverrides。prefix,suffix用于补齐sql前后缀的值。而prefixOverrides,suffixOverrides则表示如果sql语句中前缀或者后缀的值跟两个属性中的值一致,则会将这个值删除掉。看下面一段Mapper配置,该语句最终会变成select id,name,age from sstudent where id=? and 1=1.观察下列代码,进行流程分析。

  1. 在第一个trim标签中,存在prefix属性,那么语句拼凑为 where id=#{id}
  2. 检查suffix和suffixOverrides,suffixOverrides值为空,那么不做任何处理,此时会继续拼接suffix的值,所以sql语句变成where id=#{id} and
  3. 在第二个trim标签中,存在 prefixOverrides="and|or|where",mybatis首先使用竖线‘|‘分割为数组,因为1=1 and并不以{and,or,where}开头,所以不做任何处理
  4. 在 suffixOverrides="and|or|where"中能够匹配到 1=1 and中的 and后缀,那么删除and,sql片段变为1=1
  5. 拼接三段sql代码,最后结果为:select id,name,age from sstudent where id=? and 1=1
<!--测试Trim用法 -->
<select id="getStudentsByTrim" resultType="student" useCache="false" parameterType="string">
select id,name,age from student
<if test="#{id} !=null">
<trim prefix="where" suffix="and" suffixOverrides="">
id=#{id}
</trim>
<trim prefixOverrides="and|or|where" prefix="" suffixOverrides="and|or|where">
1=1 and
</trim>
</if>
</select>

trim标签原理实现

   mybatis配置文件中存在,,,,等标签,在mybatis代码中存在与之对应的实体类,在mybatis中,存在SqlNode接口,该接口下存在多个与mapper配置文件对应的实体类。观察如下结构图,可以看到TrimSqlNode下存在SetSqlNode和WhereSqlNode,这意味着where和set标签可能仍然采用前缀拼接方式组装sql语句。

分析TrimSqlNode源码

   TrimSqlNode类中有五个属性,对应这当前的节点类型,前后缀属性值和Configuration类,在TrimSqlNode构造器中,调用了parseOverrides()方法进行解析,可以看到,该方法会将待匹配的prefixesToOverride值按|进行分割并放入list中。

 1  private final SqlNode contents;//当前节点
 2 private final String prefix;//前缀名
 3 private final String suffix;//后缀名
 4 private final List<String> prefixesToOverride;//待覆盖前缀
 5 private final List<String> suffixesToOverride;//待覆盖后缀
 6 private final Configuration configuration;
 7
 8 public TrimSqlNode(Configuration configuration, SqlNode contents, String prefix, String prefixesToOverride, String suffix, String suffixesToOverride) {
 9 this(configuration, contents, prefix, parseOverrides(prefixesToOverride), suffix, parseOverrides(suffixesToOverride));
10 }
11
12 /**
13 * 按竖线切割字符串
14 * @param overrides
15 * @return
16 */
17 private static List<String> parseOverrides(String overrides) {
18 if (overrides != null) {
19 //字符串分析器,由JDK1.0提供,作用跟split()方法相同
20 final StringTokenizer parser = new StringTokenizer(overrides, "|", false);
21 final List<String> list = new ArrayList<String>(parser.countTokens());
22 while (parser.hasMoreTokens()) {
23 list.add(parser.nextToken().toUpperCase(Locale.ENGLISH));
24 }
25 return list;
26 }
27 return Collections.emptyList();
28 }

在一切属性都填充完毕后,TrimSqlNode就开始正式使用下列方法解析和组装sql语句了。

 @Override
public boolean apply(DynamicContext context) {
FilteredDynamicContext filteredDynamicContext = new FilteredDynamicContext(context);
boolean result = contents.apply(filteredDynamicContext);
filteredDynamicContext.applyAll();
return result;
}

   可以看到该方法中实例化了FilteredDynamicContext类,并调用了该类中的applyAll()方法,applyAll()方法中存在applyPrefix(),applySuffix()专门用来解析trim标签的属性。这两个方法类似,先遍历分割好的prefixesToOverride或者suffixesToOverride的值,如果待解析的sql存在要删除的前缀或者后缀,则调用delete方法进行删除,然后跳出该循环,这也就是说最多只能删除一个后缀。在完成前后缀删除后,再分别进行前后缀拼接。

private void applyPrefix(StringBuilder sql, String trimmedUppercaseSql) {
if (!prefixApplied) {
prefixApplied = true;
if (prefixesToOverride != null) {
for (String toRemove : prefixesToOverride) {
if (trimmedUppercaseSql.startsWith(toRemove)) {
sql.delete(0, toRemove.trim().length());
break;
}
}
}
if (prefix != null) {
sql.insert(0, " ");
sql.insert(0, prefix);
}
}
}

private void applySuffix(StringBuilder sql, String trimmedUppercaseSql) {
if (!suffixApplied) {
suffixApplied = true;
if (suffixesToOverride != null) {
for (String toRemove : suffixesToOverride) {
if (trimmedUppercaseSql.endsWith(toRemove) || trimmedUppercaseSql.endsWith(toRemove.trim())) {
int start = sql.length() - toRemove.trim().length();
int end = sql.length();
sql.delete(start, end);
break;
}
}
}
if (suffix != null) {
sql.append(" ");
sql.append(suffix);
}
}
}

where 和Set标签

从SqlNode的继承关系可以看到,WhereSqlNode和SetSqlNode都继承了TrimSqlNode,猜想这两个类是否直接调用了父类方法,采用applyFrefix()进行前缀拼接,查看这两个类中的源码。可以看到确实调用了父类相关方法,如果使用set,在执行update操作时,会自动补全set关键字,同时补全后缀符‘,‘,如果使用where,在执行条件查询时,会自动补全where关键字,如果后续sql语句前缀带有AND,OR类似关键字,则删除sql前缀。

public class SetSqlNode extends TrimSqlNode {

  private static List<String> suffixList = Arrays.asList(",");

  public SetSqlNode(Configuration configuration,SqlNode contents) {
    super(configuration, contents, "SET", null, null, suffixList);
  }

}

public class WhereSqlNode extends TrimSqlNode {

  private static List<String> prefixList = Arrays.asList("AND ","OR ","AND\n", "OR\n", "AND\r", "OR\r", "AND\t", "OR\t");

  public WhereSqlNode(Configuration configuration, SqlNode contents) {
    super(configuration, contents, "WHERE", prefixList, null, null);
  }

}

原文地址:https://www.cnblogs.com/zhengzuozhanglina/p/11307357.html

时间: 2024-11-09 03:12:19

Mybatis源码分析:trim标签的相关文章

【MyBatis源码分析】环境准备

前言 之前一段时间写了[Spring源码分析]系列的文章,感觉对Spring的原理及使用各方面都掌握了不少,趁热打铁,开始下一个系列的文章[MyBatis源码分析],在[MyBatis源码分析]文章的基础之上,可以继续分析数据库连接池.Spring整合MyBatis源码.Spring事物管理tx等等. [MyBatis源码分析]整个文章结构相较[Spring源码分析]稍微改一改,后者会在每一部分源码分析的开头列出要分析的源码的实例,比如: 分析Bean流程加载,就会先写Bean的代码示例及xml

【MyBatis源码分析】select源码分析及小结

示例代码 之前的文章说过,对于MyBatis来说insert.update.delete是一组的,因为对于MyBatis来说它们都是update:select是一组的,因为对于MyBatis来说它就是select. 本文研究一下select的实现流程,示例代码为: 1 public void testSelectOne() { 2 System.out.println(mailDao.selectMailById(8)); 3 } selectMailById方法的实现为: 1 public M

MyBatis 源码分析 - 配置文件解析过程

* 本文速览 由于本篇文章篇幅比较大,所以这里拿出一节对本文进行快速概括.本篇文章对 MyBatis 配置文件中常用配置的解析过程进行了较为详细的介绍和分析,包括但不限于settings,typeAliases和typeHandlers等,本文的篇幅也主要在对这三个配置解析过程的分析上.下面,我们来一起看一下本篇文章的目录结构. 从目录上可以看出,2.3节.2.5节和2.8节的内容比较多.其中2.3节是关于settings配置解析过程的分析,除了对常规的 XML 解析过程分析,本节额外的分析了元

Mybatis源码分析之Cache二级缓存原理 (五)

一:Cache类的介绍 讲解缓存之前我们需要先了解一下Cache接口以及实现MyBatis定义了一个org.apache.ibatis.cache.Cache接口作为其Cache提供者的SPI(ServiceProvider Interface) ,所有的MyBatis内部的Cache缓存,都应该实现这一接口 Cache的实现类中,Cache有不同的功能,每个功能独立,互不影响,则对于不同的Cache功能,这里使用了装饰者模式实现. 看下cache的实现类,如下图: 1.FIFOCache:先进

mybatis源码分析(一)

mybatis源码分析(sqlSessionFactory生成过程) 1. mybatis框架在现在各个IT公司的使用不用多说,这几天看了mybatis的一些源码,赶紧做个笔记. 2. 看源码从一个demo引入如下: public class TestApp { private static SqlSessionFactory sqlSessionFactory; static { InputStream inputStream; String resource = "mybatis-confi

MyBatis源码分析-MyBatis初始化流程

MyBatis 是支持定制化 SQL.存储过程以及高级映射的优秀的持久层框架.MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集.MyBatis 可以对配置和原生Map使用简单的 XML 或注解,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录.如何新建MyBatis源码工程请点击MyBatis源码分析-IDEA新建MyBatis源码工程. MyBatis初始化的过程也就是创建Configura

mybatis源码分析之cache创建

XMLMapperBuilder.java //解析<cache /> 配置元素,创建cache对象 private void cacheElement(XNode context) throws Exception {     if (context != null) {       String type = context.getStringAttribute("type", "PERPETUAL");       Class<? exten

MyBatis源码分析-SQL语句执行的完整流程

MyBatis 是支持定制化 SQL.存储过程以及高级映射的优秀的持久层框架.MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集.MyBatis 可以对配置和原生Map使用简单的 XML 或注解,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录.如何新建MyBatis源码工程请点击MyBatis源码分析-IDEA新建MyBatis源码工程. MyBatis框架主要完成的是以下2件事情: 根据JD

MyBatis 源码分析 - 插件机制

1.简介 一般情况下,开源框架都会提供插件或其他形式的拓展点,供开发者自行拓展.这样的好处是显而易见的,一是增加了框架的灵活性.二是开发者可以结合实际需求,对框架进行拓展,使其能够更好的工作.以 MyBatis 为例,我们可基于 MyBatis 插件机制实现分页.分表,监控等功能.由于插件和业务无关,业务也无法感知插件的存在.因此可以无感植入插件,在无形中增强功能. 开发 MyBatis 插件需要对 MyBatis 比较深了解才行,一般来说最好能够掌握 MyBatis 的源码,门槛相对较高.本篇