博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
mybatis中动态SQL之trim详解
阅读量:6314 次
发布时间:2019-06-22

本文共 6233 字,大约阅读时间需要 20 分钟。

一. 背景

  之前mybatis中<where>、<update>、<if>、<foreach>标签用的多,知道有<trim>这个标签,但很少去用,也没有去深入理解它,直到最近遇到一个问题。问题是这样的:

  一个SQL有三个int查询字段a、b、c,表达式为:a=#{a} AND (b=#{b} OR c=#{c})。其中a是必查的,b和c为非必查的(这里假定传入-1表示该字段不参与查询)。那么该表达式会有以下几种形态:

  • a=#{a}
  • a=#{a} AND b=#{b}
  • a=#{a} AND c=#{c}
  • a=#{a} AND (b=#{b} OR c=#{c})

  看到这个需求后,觉得逻辑还是挺简单的,但写起mapper的SQL来并不是那么容易(如果你有的话,欢迎下边评论贴出来)。考虑了多层<if>、<choose>等标签,虽然也能实现这个功能,但过于繁琐。有没有一种更简单的实现方式?有!<trim>!!!

  请尊重作者劳动成果,转载请标明原文链接:

二. 功能描述与用法

  网上关于<trim>的介绍并不多,通过看mybatis的源码,一句话描述trim的功能:子句首尾的删除与添加。它就是一个字符串处理工具,类似于replace(),但它只处理首尾。真的是一个神器,其实mybatis中的<set>和<where>都可以用<trim>来实现,但<trim>的功能更强大,使用起来更灵活!!!

 

  <trim prefix="(" prefixOverrides="OR" suffixOverrides="," suffix=")">子句</trim>

  这里的子句会对其进行trim()处理,忽略掉换行、空格等字符,所以本文中的子句都是指trim()处理后的字符串。如果子句为空,那么整个<trim>块不起作用,相当于不存在。本<trim>块的作用就是:去掉子句首的OR和子句尾的逗号,并在子句前后分别加上(和),比如,"orabc,"-->"(abc)"。

  • prefixOverrides:子句首的命中词列表,以|分隔,忽略大小写。如果命中(轮询命中词,最多只命中一次),会删除子句首命中的词;没命中就算了。
  • prefix:删除子句句首后,在子句最前边加上单个空格+prefix。
  • suffixOverrides:子句尾的命中词列表,以|分隔,忽略大小写。如果命中(轮询命中词,最多只命中一次),会删除子句尾命中的词;没命中就算了。
  • suffix:删除子句句尾后,在子句最后边加上单个空格+suffix。

  有了这个神器,处理前文提起的需求,就可以用<trim>很悠然的处理了。 

WHERE a = #{a}	
OR b = #{b}
OR c = #{c}

 

  <trim>的实现非常简单,如果觉得我描述的还不够清楚,可以看下第三部分的源码。

三.源码(org.apache.ibatis.scripting.xmltags.TrimSqlNode) 

/** *    Copyright 2009-2015 the original author or authors. * *    Licensed under the Apache License, Version 2.0 (the "License"); *    you may not use this file except in compliance with the License. *    You may obtain a copy of the License at * *       http://www.apache.org/licenses/LICENSE-2.0 * *    Unless required by applicable law or agreed to in writing, software *    distributed under the License is distributed on an "AS IS" BASIS, *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *    See the License for the specific language governing permissions and *    limitations under the License. */package org.apache.ibatis.scripting.xmltags;import java.util.ArrayList;import java.util.Collections;import java.util.List;import java.util.Locale;import java.util.Map;import java.util.StringTokenizer;import org.apache.ibatis.session.Configuration;/** * @author Clinton Begin */public class TrimSqlNode implements SqlNode {  private SqlNode contents;  private String prefix;//前缀  private String suffix;//后缀  private List
prefixesToOverride;//子句首命中词列表 private List
suffixesToOverride;//子句尾命中词列表 private Configuration configuration; public TrimSqlNode(Configuration configuration, SqlNode contents, String prefix, String prefixesToOverride, String suffix, String suffixesToOverride) { this(configuration, contents, prefix, parseOverrides(prefixesToOverride), suffix, parseOverrides(suffixesToOverride));//解析以|分隔的命中词 } protected TrimSqlNode(Configuration configuration, SqlNode contents, String prefix, List
prefixesToOverride, String suffix, List
suffixesToOverride) { this.contents = contents; this.prefix = prefix; this.prefixesToOverride = prefixesToOverride; this.suffix = suffix; this.suffixesToOverride = suffixesToOverride; this.configuration = configuration; } @Override public boolean apply(DynamicContext context) { FilteredDynamicContext filteredDynamicContext = new FilteredDynamicContext(context); boolean result = contents.apply(filteredDynamicContext); filteredDynamicContext.applyAll();//解析
return result; } //将命中词以|分隔,获取命中词列表 private static List
parseOverrides(String overrides) { if (overrides != null) { final StringTokenizer parser = new StringTokenizer(overrides, "|", false); final List
list = new ArrayList
(parser.countTokens()); while (parser.hasMoreTokens()) { list.add(parser.nextToken().toUpperCase(Locale.ENGLISH));//命中词统一转为大写。 } return list; } return Collections.emptyList(); } private class FilteredDynamicContext extends DynamicContext { private DynamicContext delegate; private boolean prefixApplied;//前缀处理标记,表示是否处理过。 private boolean suffixApplied;//后缀处理标记,表示是否处理过。 private StringBuilder sqlBuffer; public FilteredDynamicContext(DynamicContext delegate) { super(configuration, null); this.delegate = delegate; this.prefixApplied = false; this.suffixApplied = false; this.sqlBuffer = new StringBuilder(); } public void applyAll() { sqlBuffer = new StringBuilder(sqlBuffer.toString().trim());//子句先trim() String trimmedUppercaseSql = sqlBuffer.toString().toUpperCase(Locale.ENGLISH);//跟命中词一致,子句也统一转为大写,所以
对子句的处理是忽略大小写的。 if (trimmedUppercaseSql.length() > 0) { //如果子句非空才处理 applyPrefix(sqlBuffer, trimmedUppercaseSql);//处理前缀 applySuffix(sqlBuffer, trimmedUppercaseSql);//处理后缀 } delegate.appendSql(sqlBuffer.toString()); } @Override public Map
getBindings() { return delegate.getBindings(); } @Override public void bind(String name, Object value) { delegate.bind(name, value); } @Override public int getUniqueNumber() { return delegate.getUniqueNumber(); } @Override public void appendSql(String sql) { sqlBuffer.append(sql); } @Override public String getSql() { return delegate.getSql(); } 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); } } } }}

 

转载于:https://www.cnblogs.com/waterystone/p/7070836.html

你可能感兴趣的文章
Eclipse插件大全 挑选最牛的TOP30
查看>>
一些实用性的总结与纠正
查看>>
Kubernetes概念
查看>>
逻辑卷管理器(LVM)
查看>>
一个小代码,欢迎大佬的意见,求指正
查看>>
神经网络注意力机制--Attention in Neural Networks
查看>>
Spring.Net+WCF实现分布式事务
查看>>
个人简历-项目经验
查看>>
swoole异步任务task处理慢请求简单实例
查看>>
oracle数据泵导入分区表统计信息报错(四)
查看>>
spring技术内幕读书笔记之IoC容器的学习
查看>>
细说多线程(五) —— CLR线程池的I/O线程
查看>>
JavaScript instanceof和typeof的区别
查看>>
Hadoop文件系统详解-----(一)
查看>>
状态码
查看>>
我的友情链接
查看>>
用sqlplus远程连接oracle命令
查看>>
多年一直想完善的自由行政审批流程组件【2002年PHP,2008年.NET,2010年完善数据设计、代码实现】...
查看>>
自动生成四则运算题目
查看>>
【前台】【单页跳转】整个项目实现单页面跳转,抛弃iframe
查看>>