学习笔记,选自Mybatis官方中文文档:http://www.mybatis.org/mybatis-3/zh/dynamic-sql.html

  MyBatis 的强大特性之一便是它的动态 SQL。如果你有使用 JDBC 或其他类似框架的经验,你就能体会到根据不同条件拼接 SQL 语句有多么痛苦。拼接的时候要确保不能忘了必要的空格,还要注意省掉列名列表最后的逗号。利用动态 SQL 这一特性可以彻底摆脱这种痛苦。

  通常使用动态 SQL 不可能是独立的一部分,MyBatis 当然使用一种强大的动态 SQL 语言来改进这种情形,这种语言可以被用在任意的 SQL 映射语句中。

  动态 SQL 元素和使用 JSTL 或其他类似基于 XML 的文本处理器相似。在 MyBatis 之前的版本中,有很多的元素需要来了解。MyBatis 3 大大提升了它们,现在用不到原先一半的元素就可以了。MyBatis 采用功能强大的基于 OGNL 的表达式来消除其他元素。

  • if
  • choose (when, otherwise)
  • trim (where, set)
  • foreach

if

  动态 SQL 通常要做的事情是有条件地包含 where 子句的一部分。比如:

 <select id="findActiveBlogWithTitleLike"
      resultType="Blog">
   SELECT * FROM BLOG
   WHERE state = ‘ACTIVE’
   <if test="title != null">
     AND title like #{title}
   </if>
 </select>

  这条语句提供了一个可选的文本查找类型的功能。如果没有传入“title”,那么所有处于“ACTIVE”状态的BLOG都会返回;反之若传入了“title”,那么就会把模糊查找“title”内容的BLOG结果返回(就这个例子而言,细心的读者会发现其中的参数值是可以包含一些掩码或通配符的)。

  如果想可选地通过“title”和“author”两个条件搜索该怎么办呢?首先,改变语句的名称让它更具实际意义;然后只要加入另一个条件即可。

 <select id="findActiveBlogLike"
      resultType="Blog">
   SELECT * FROM BLOG WHERE state = ‘ACTIVE’
   <if test="title != null">
     AND title like #{title}
   </if>
   <if test="author != null and author.name != null">
     AND author_name like #{author.name}
   </if>
 </select>

choose, when, otherwise

  有些时候,我们不想用到所有的条件语句,而只想从中择其一二。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。

  还是上面的例子,但是这次变为提供了“title”就按“title”查找,提供了“author”就按“author”查找,若两者都没有提供,就返回所有符合条件的BLOG(实际情况可能是由管理员按一定策略选出BLOG列表,而不是返回大量无意义的随机结果)。

 <select id="findActiveBlogLike"
      resultType="Blog">
   SELECT * FROM BLOG WHERE state = ‘ACTIVE’
   <choose>
     <when test="title != null">
       AND title like #{title}
     </when>
     <when test="author != null and author.name != null">
       AND author_name like #{author.name}
     </when>
     <otherwise>
       AND featured = 1
     </otherwise>
   </choose>
 </select>

trim, where, set

  前面几个例子已经合宜地解决了一个臭名昭著的动态 SQL 问题。现在考虑回到“if”示例,这次我们将“ACTIVE = 1”也设置成动态的条件,看看会发生什么。

 <select id="findActiveBlogLike"
      resultType="Blog">
   SELECT * FROM BLOG
   WHERE
   <if test="state != null">
     state = #{state}
   </if>
   <if test="title != null">
     AND title like #{title}
   </if>
   <if test="author != null and author.name != null">
     AND author_name like #{author.name}
   </if>
 </select>

  如果这些条件没有一个能匹配上将会怎样?最终这条 SQL 会变成这样:

 SELECT * FROM BLOG
 WHERE

  这会导致查询失败。如果仅仅第二个条件匹配又会怎样?这条 SQL 最终会是这样:

 SELECT * FROM BLOG
 WHERE
 AND title like ‘someTitle’

  这个查询也会失败。这个问题不能简单的用条件句式来解决,如果你也曾经被迫这样写过,那么你很可能从此以后都不想再这样去写了。

  MyBatis 有一个简单的处理,这在90%的情况下都会有用。而在不能使用的地方,你可以自定义处理方式来令其正常工作。一处简单的修改就能得到想要的效果:

 <select id="findActiveBlogLike"
      resultType="Blog">
   SELECT * FROM BLOG
   <where>
     <if test="state != null">
          state = #{state}
     </if>
     <if test="title != null">
         AND title like #{title}
     </if>
     <if test="author != null and author.name != null">
         AND author_name like #{author.name}
     </if>
   </where>
 </select>

  where 元素知道只有在一个以上的if条件有值的情况下才去插入“WHERE”子句。而且,若最后的内容是“AND”或“OR”开头的,where 元素也知道如何将他们去除。

  如果 where 元素没有按正常套路出牌,我们还是可以通过自定义 trim 元素来定制我们想要的功能。比如,和 where 元素等价的自定义 trim 元素为:

 <trim prefix="WHERE" prefixOverrides="AND |OR ">
   ...
 </trim>

  prefixOverrides 属性会忽略通过管道分隔的文本序列(注意此例中的空格也是必要的)。它带来的结果就是所有在 prefixOverrides 属性中指定的内容将被移除,并且插入 prefix 属性中指定的内容。

  类似的用于动态更新语句的解决方案叫做 set。set 元素可以被用于动态包含需要更新的列,而舍去其他的。比如:

 <update id="updateAuthorIfNecessary">
   update Author
     <set>
       <if test="username != null">username=#{username},</if>
       <if test="password != null">password=#{password},</if>
       <if test="email != null">email=#{email},</if>
       <if test="bio != null">bio=#{bio}</if>
     </set>
   where id=#{id}
 </update>

  这里,set 元素会动态前置 SET 关键字,同时也会消除无关的逗号,因为用了条件语句之后很可能就会在生成的赋值语句的后面留下这些逗号。

  若你对等价的自定义 trim 元素的样子感兴趣,那这就应该是它的真面目:

 <trim prefix="SET" suffixOverrides=",">
   ...
 </trim>

  注意这里我们忽略的是后缀中的值,而又一次附加了前缀中的值。

foreach

  动态 SQL 的另外一个常用的必要操作是需要对一个集合进行遍历,通常是在构建 IN 条件语句的时候。比如:

 <select id="selectPostIn" resultType="domain.blog.Post">
   SELECT *
   FROM POST P
   WHERE ID in
   <foreach item="item" index="index" collection="list"
       open="(" separator="," close=")">
         #{item}
   </foreach>
 </select>

  foreach 元素的功能是非常强大的,它允许你指定一个集合,声明可以用在元素体内的集合项和索引变量。它也允许你指定开闭匹配的字符串以及在迭代中间放置分隔符。这个元素是很智能的,因此它不会偶然地附加多余的分隔符。

  你可以将任何可迭代对象(如列表、集合等)和任何的字典或者数组对象传递给foreach作为集合参数。当使用可迭代对象或者数组时,index是当前迭代的次数,item的值是本次迭代获取的元素。当使用字典(或者Map.Entry对象的集合)时,index是键,item是值。

  到此我们已经完成了涉及 XML 配置文件和 XML 映射文件的讨论。下一部分将详细探讨 Java API,这样才能从已创建的映射中获取最大利益。

bind

  bind 元素可以从 OGNL 表达式中创建一个变量并将其绑定到上下文。比如:

 <select id="selectBlogsLike" resultType="Blog">
   <bind name="pattern" value="'%' + _parameter.getTitle() + '%'" />
   SELECT * FROM BLOG
   WHERE title LIKE #{pattern}
 </select>

Multi-db vendor support

  一个配置了“_databaseId”变量的 databaseIdProvider 对于动态代码来说是可用的,这样就可以根据不同的数据库厂商构建特定的语句。比如下面的例子:

 <insert id="insert">
   <selectKey keyProperty="id" resultType="int" order="BEFORE">
     <if test="_databaseId == 'oracle'">
       select seq_users.nextval from dual
     </if>
     <if test="_databaseId == 'db2'">
       select nextval for seq_users from sysibm.sysdummy1"
     </if>
   </selectKey>
   insert into users values (#{id}, #{name})
 </insert>

动态 SQL 中可插拔的脚本语言

  MyBatis 从 3.2 开始支持可插拔的脚本语言,因此你可以在插入一种语言的驱动(language driver)之后来写基于这种语言的动态 SQL 查询。

  可以通过实现下面接口的方式来插入一种语言:

 public interface LanguageDriver {
   ParameterHandler createParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql);
   SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType);
   SqlSource createSqlSource(Configuration configuration, String script, Class<?> parameterType);
 }

  一旦有了自定义的语言驱动,你就可以在 mybatis-config.xml 文件中将它设置为默认语言:

 <typeAliases>
   <typeAlias type="org.sample.MyLanguageDriver" alias="myLanguage"/>
 </typeAliases>
 <settings>
   <setting name="defaultScriptingLanguage" value="myLanguage"/>
 </settings>

  除了设置默认语言,你也可以针对特殊的语句指定特定语言,这可以通过如下的 lang 属性来完成:

 <select id="selectBlog" lang="myLanguage">
   SELECT * FROM BLOG
 </select>

  或者在你正在使用的映射中加上注解 @Lang 来完成:

 <select id="selectBlog" lang="myLanguage">
   SELECT * FROM BLOG
 </select>

  你前面看到的所有 xml 标签都是默认 MyBatis 语言提供的,它是由别名为 xml 语言驱动器 org.apache.ibatis.scripting.xmltags.XmlLanguageDriver 驱动的。

Mybatis学习--动态SQL的更多相关文章

  1. MyBatis学习——动态SQL

    开发人员在使用JDBC框架或者其他类似的框架进行数据库开发时,通常都要根据需求去手动拼接SQL,这样非常麻烦,而myBatis提供了对SQL语句动态组装的功能,恰好解决了这一问题. 一,动态SQL中的 ...

  2. MyBatis 学习-动态 SQL 篇

    MyBatis 为我们提供了如下几个动态 SQL 元素: if choose foreach where/set trim 一.IF 元素 <select id="selectProj ...

  3. Java-MyBatis:MyBatis 3 动态 SQL

    ylbtech-Java-MyBatis:MyBatis 3 动态 SQL 1.返回顶部 1. 动态 SQL MyBatis 的强大特性之一便是它的动态 SQL.如果你有使用 JDBC 或其它类似框架 ...

  4. MyBatis 构造动态 SQL 语句

    以前看过一个本书叫<深入浅出 MFC >,台湾 C++ 大师写的一本书.在该书中写到这样一句话,“勿在浮沙筑高台”,这句话写的的确对啊.编程很多语言虽然相通,但是真正做还是需要认真的学习, ...

  5. MyBatis的动态SQL详解

    MyBatis的动态SQL是基于OGNL表达式的,它可以帮助我们方便的在SQL语句中实现某些逻辑,本文详解mybatis的动态sql,需要的朋友可以参考下 MyBatis 的一个强大的特性之一通常是它 ...

  6. Mybatis解析动态sql原理分析

    前言 废话不多说,直接进入文章. 我们在使用mybatis的时候,会在xml中编写sql语句. 比如这段动态sql代码: <update id="update" parame ...

  7. mybatis 使用动态SQL

    RoleMapper.java public interface RoleMapper { public void add(Role role); public void update(Role ro ...

  8. MyBatis框架——动态SQL、缓存机制、逆向工程

    MyBatis框架--动态SQL.缓存机制.逆向工程 一.Dynamic SQL 为什么需要动态SQL?有时候需要根据实际传入的参数来动态的拼接SQL语句.最常用的就是:where和if标签 1.参考 ...

  9. 使用Mybatis实现动态SQL(一)

    使用Mybatis实现动态SQL 作者 : Stanley 罗昊 [转载请注明出处和署名,谢谢!] 写在前面:        *本章节适合有Mybatis基础者观看* 前置讲解 我现在写一个查询全部的 ...

随机推荐

  1. String、StringBuilder、 StringBuffer 深入分析 源代码解析

    java学习有一段时间了.但学习的东西都是框架等东西,java基础知识有点遗忘.所以重温一下java基础知识.写写文章里面有错的希望大家指正共同进步~~ 一.String 大家常常会说使用" ...

  2. local variable 'xxx' referenced before assignment(犯过同样的错)

    这个问题很囧,在外面定义了一个变量 xxx ,然后在Python的一个函数里面引用这个变量,并改变它的值,结果报错local variable 'xxx' referenced before assi ...

  3. python 基础 9.6 设计表结构

    一. 设计表结构    在操作设计数据库之前,我们先要设计数据库表结构,我们就来分析分析经典的学生,课程,成绩,老师这几者他们之间的关系,我们先来分析各个主体他们直接有什么属性,并确定表结构,在实际开 ...

  4. 【BZOJ4769】超级贞鱼 归并排序求逆序对

    [BZOJ4769]超级贞鱼 Description 马达加斯加贞鱼是一种神奇的双脚贞鱼,它们把自己的智慧写在脚上——每只贞鱼的左脚和右脚上各有一个数.有一天,K只贞鱼兴致来潮,排成一列,从左到右第i ...

  5. 解决 SVN版本冲突

    链接:http://blog.csdn.net/windone0109/article/details/4857044 版本冲突原因: 假设A.B两个用户都在版本号为100的时候,更新了kingtun ...

  6. c++操作flash

    c++操作falsh,忘了原文在哪了,自己尝试了,直接贴代码 // SDK版本 //////////////////////////////////////////////////////////// ...

  7. Dajngo admin使用

    Dajngo admin使用 Django 提供了基于 web 的管理工具. Django 自动管理工具是 django.contrib 的一部分.你可以在项目的 settings.py 中的 INS ...

  8. Linux开启防火墙后,设置允许通过的端口

    安装Firewall命令: yum install firewalld firewalld-config Firewall开启端口命令: firewall-cmd --zone=public --ad ...

  9. LDA中的先验知识

    LDA涉及到的先验知识有:二项分布.Gamma函数.Beta分布.多项分布.Dirichlet分布.马尔科夫链.MCMC.Gibbs Sampling.EM算法等. 二项分布 二项分布是N重伯努利分布 ...

  10. Arduino 看门狗使用

    1.需要调用 #include <avr/wdt.h> 2.设置看门狗复位时间 wdt_enable(WDTO_2S); 代码时间定义的底层查看 #define WDTO_15MS 0 / ...