mybatis中动态SQL之trim详解
一. 背景
之前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>!!!
请尊重作者劳动成果,转载请标明原文链接:http://www.cnblogs.com/waterystone/p/7070836.html
二. 功能描述与用法
网上关于<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}
<trim prefix="AND(" prefixOverrides="OR" suffix=")">
<if test="b != -1">
OR b = #{b}
</if>
<if test="c != -1">
OR c = #{c}
</if>
</trim>
<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<String> prefixesToOverride;//子句首命中词列表
private List<String> 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<String> prefixesToOverride, String suffix, List<String> 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();//解析<trim>
return result;
} //将命中词以|分隔,获取命中词列表
private static List<String> parseOverrides(String overrides) {
if (overrides != null) {
final StringTokenizer parser = new StringTokenizer(overrides, "|", false);
final List<String> list = new ArrayList<String>(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);//跟命中词一致,子句也统一转为大写,所以<trim>对子句的处理是忽略大小写的。
if (trimmedUppercaseSql.length() > 0) {//如果子句非空才处理
applyPrefix(sqlBuffer, trimmedUppercaseSql);//处理前缀
applySuffix(sqlBuffer, trimmedUppercaseSql);//处理后缀
}
delegate.appendSql(sqlBuffer.toString());
} @Override
public Map<String, Object> 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);
}
}
} } }
mybatis中动态SQL之trim详解的更多相关文章
- MyBatis中动态SQL元素的使用
掌握MyBatis中动态SQL元素的使用 if choose(when,otherwise) trim where set foreach <SQL>和<include> 在应 ...
- Mybatis中动态SQL多条件查询
Mybatis中动态SQL多条件查询 mybatis中用于实现动态SQL的元素有: if:用if实现条件的选择,用于定义where的字句的条件. choose(when otherwise)相当于Ja ...
- Mybatis中动态SQL语句中的parameterType不同数据类型的用法
Mybatis中动态SQL语句中的parameterType不同数据类型的用法1. 简单数据类型, 此时#{id,jdbcType=INTEGER}中id可以取任意名字如#{a,jdbcType ...
- MyBatis中动态SQL语句完成多条件查询
一看这标题,我都感觉到是mybatis在动态SQL语句中的多条件查询是多么的强大,不仅让我们用SQL语句完成了对数据库的操作:还通过一些条件选择语句让我们SQL的多条件.动态查询更加容易.简洁.直观. ...
- MyBatis中传入参数parameterType类型详解
前言 Mybatis的Mapper文件中的select.insert.update.delete元素中有一个parameterType属性,用于对应的mapper接口方法接受的参数类型.本文主要给大家 ...
- Mybatis mapper动态代理的原理详解
在开始动态代理的原理讲解以前,我们先看一下集成mybatis以后dao层不使用动态代理以及使用动态代理的两种实现方式,通过对比我们自己实现dao层接口以及mybatis动态代理可以更加直观的展现出my ...
- 阶段3 1.Mybatis_08.动态SQL_03.mybatis中动态sql语句-foreach和sql标签
foreach标签 in的查询 sql语句好写,但是传参在映射文件里面改怎么传呢 定义一个List<Integer>成员变量,然后生成get和set 定义一个新的查询方法 open:开始符 ...
- 阶段3 1.Mybatis_08.动态SQL_02.mybatis中动态sql语句-where标签的使用
这里的userSex是实体类里面的属性名,而不是数据库内的字段名称 一个老王改成性别女,为了区分一下 增加sex字段的查询 where标签 用上where和刚才的执行效果是一样的 where标签使我们 ...
- Mybatis的动态SQL实现
一.动态SQL简介 MyBatis的强大特性之一便是它的动态 SQL.如果你有使用 JDBC 或其他类似框架的经验,你就能体会到根据不同条件拼接 SQL 语句有多么痛苦.拼接的时候要确保不能忘了必要的 ...
随机推荐
- 201621123002《JAVA程序设计》第三章学习总结
1. 本周学习总结 1.1 写出你认为本周学习中比较重要的知识点关键词,如类.对象.封装等 关键词:类 对象 封装 构造函数 this,static,final 1.2 用思维导图或者Onenote或 ...
- Vc 检测内存泄漏
启用内存泄漏检测 检测内存泄漏是 C/c + + 调试器和 C 运行时库 (CRT) 的主要工具调试堆函数. 若要启用调试堆的所有函数,在 c + + 程序中,按以下顺序包含以下语句: C++复制 # ...
- Ural 1018 Binary Apple Tree
题目链接:http://acm.timus.ru/problem.aspx?space=1&num=1018 Dynamic Programming. 首先要根据input建立树形结构,然后在 ...
- Jmeter利用CSV Data Set Config 和 _CSVRead函数实现参数化
测试接口时,当请求参数不是固定的,每次请求对应的参数都是不同的,就需要对请求参数进行参数化.我们这里简单介绍一下5个用户与一个用户建立好友关系. 一.测试需求描述 1. 本次测试的接口为http服务端 ...
- VM无法连接到虚拟机
The VMware Authorization Service is not running. 原因 虚拟机服务没有开启 解决方法 1. 我的电脑右击->管理 2. 打开服 ...
- ubuntu服务器搭建DVWA站点
把DVWA里面的文件夹拷贝到/var/www/html里面去 1.安装之前先更新系统 sudo apt update 2.安装Apache2 sudo apt install apache2 3.安装 ...
- Unity3D之物理射线
射线:从一个点往一个方向,发射一根无限长的射线,这根射线会与场景中带有 Collider 组件的物体发生碰撞. 射线的使用: public class Test { private void Star ...
- Note | LaTeX
目录 一.TeX家族 1. TeX - LaTeX 2. pdfTeX - pdfLaTeX 3. XeTeX - XeLaTeX 4. CTeX - MiKTeX - TeX Live 二.入门 1 ...
- 设置UniDbGrid的整行显示颜色,如果某字段值是我们的控制字段
设置UniDbGrid的整行显示颜色,如果某字段值是我们的控制字段,使用下列判断设置更快捷一点: procedure TUniForm.UniDBGridDrawColumnCell(Sender: ...
- LeNet,AlexNet,GoogleLeNet,VggNet等网络对比
CNN的发展史 上一篇回顾讲的是2006年Hinton他们的Science Paper,当时提到,2006年虽然Deep Learning的概念被提出来了,但是学术界的大家还是表示不服.当时有流传的段 ...