当前位置: 代码迷 >> SQL >> Mybatis施用:Dynamic Sql
  详细解决方案

Mybatis施用:Dynamic Sql

热度:90   发布时间:2016-05-05 12:26:04.0
Mybatis使用:Dynamic Sql

Mybatis使用:Dynamic Sql

?

本节我们学习下Mybatis最强大的特性之一。

如果你使用过JDBC来拼接SQL语句,后者有其它简单ORM框架使用经验的话,你会明白根据根据条件拼接SQL是多么痛苦,少个括号,逗号,空格都是一种灾难。你需要仔细的测试你的SQL生成的各种结果来确保SQL拼接覆盖到了各种情况。这种情况特别影响心情,还有可能将隐患带到线上。如果出现这种情况的话,你听到bug的第一印象就是动态拼接SQL的问题,抓狂的感觉可想而知啦。

而使用Mybatis的Dynamic SQL能够让你处理、远离这种痛苦。

好吧,上面这段类似宣传的说明就是说明一件事:使用mybatis吧,Dynamic SQL特性能帮助你解决条件SQL拼接的问题。当然,前提是建立在你能熟悉、熟练Mybatis的Dynamic SQL特性的基础之上。

?

说了这么多,Dynamic SQL究竟能有多强大呢,我们通过例程来看下。

注意本文的Dynamic SQL我以前用过,现在回头看下特性差别不大,就不在单独写SQL验证,以官方的SQL教程为准,会加上一些自己的理解和使用场景,还有一些教程不太容易理解的地方。官方的Dynamic SQL地址见这里:http://www.mybatis.org/core/dynamic-sql.html

?

Dynamic SQL支持下面四种条件SQL:

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

我们分别来学习下这四种条件SQL的使用。

if

这个SQL是在Dynamic SQL里面使用最多,也是最为重要的一个,使用方法倒不难,看下if条件在where条件语句中的情况:

?

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

?这个SQL提供基本的查询功能state='active',另外提供一个可选的条件title:如果你传了title这个参数,就会添加上title这个条件。

?

如果想选择多个条件的话,if语句时可以叠加使用的:

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

?

如果你添加的调试时Blog实体类中的对象的条件,如下面的author,判断author.name是否为空:

<select id="findActiveBlogLike" parameterType="Blog" 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 where other

在有些情况下,你不需要所有的条件,需要在多个条件中进行选择,如Java中的Switch或者if...else...判断:

<select id="findActiveBlogLike" parameterType="Blog" 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>

?根据test的结果添加条件,如果条件都不满足,则设定默认的条件,这个功能还是比较实用的,关键是思路清晰,易于理解。

?

trim, where, set

如果where后面的条件都是可选项怎么办?这三个参数就是解决这个问题的。这几个测试条件都比较特殊,需要仔细阅读体会。

<select id="findActiveBlogLike" parameterType="Blog" 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

?这个明显是个Error1064语法错误,如果仅仅匹配第二个条件的话,SQL回事这样:

SELECT * FROM BLOG WHERE AND title like ‘someTitle’

?这个也是个语法错误。对于where后面全部是条件判断的话,就不能用单纯的if语句了。

?

Mybatis的处理时将where也作为条件,这个是Dynamic SQL新添加的功能:

<select id="findActiveBlogLike" parameterType="Blog" 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元素能根据被包含的判断,做出是否插入的判断;并且能够判断出AND和OR开头的内容。

?

在官方文档中还有一句话,如果上面的where没有按照期望工作的话,使用trim元素来自定义。这句话比较奇怪,莫非是官方不相信这个功能的准确性。

<select id="findActiveBlogLike" parameterType="Blog" resultType="Blog">  SELECT * FROM BLOG   <trim prefix="WHERE" prefixOverrides="AND |OR ">    <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>  </trim></select>

?prefixOverrides属性会根据前缀覆盖内容,当然有前缀覆盖,也一定有后缀覆盖。

?

在使用动态更新时,使用类似的解决方案是set,set元素可以更新需要更新的列,而不需要全部更新;这个能让我们的SQL自行判断是否需要更新,如果你不设定值得花,SQL会忽略掉更新。这个会避免我们会将null值更新到数据库去,在很多情况下这个是不允许的。

<update id="updateAuthorIfNecessary" parameterType="domain.blog.Author">  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>

?更新数据时,只要设定需要更新的部分即可,不用全部重新设定。

?

相对应的trim元素可以写成这样:

<update id="updateAuthorIfNecessary" parameterType="domain.blog.Author">  update Author    <trim prefix="SET",suffixOverrides=",">      <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>    </trim>  where id=#{id}</update>

?trim会覆盖掉后缀的逗号,使得SQL能够正常提交。

?

foreach

最后一个Dynamic 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能够指定开放和关闭的标记,并且能够指定迭代之间的分隔符,这个对于获得数据库指定多个状态的数据时很有效的。

?

bind

在前面我们看到过like的例子,在哪个例子中,你需要自行添加好匹配符之后,在传给SQL。如果你只想传入Bill,来获得author.name中包含有Bill的作家名称,你需要使用bind操作OGNL表达式:

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

?你只用传入Bill,就可以查询title中包含有Bill的title;而在前面中,你需要将title设定为'%Bill%',这个操作才是真正的like含义。

?

最后说下Dynamic SQL的插件式的脚本特性,这个需要最新mybatis 3.2的支持。

mybatis支持两种内置语言,xml和raw;上面讨论的都是xml,能够支持上面提到的所有元素。

但是raw语言的话就不会支持全部;当你使用raw格式来设定SQL时,Mybatis仅仅做参数替换后就将替换参数后的SQL提交给数据库。如你所见,对于Mybatis的Dynamic SQL特性来说,raw就是一种倒退。但是raw依然存在,因为它比xml格式要快。

对于raw的使用,实际上在开发者,如果你的SQL本身就比价简单,就不用经过一系列动态SQL解析,直接提交给数据库即可,下面我们看下例程:

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

?

如果使用Annotation的话,可以这么使用:

public interface Mapper {  @Lang(RawLanguageDriver.class)  @Select("SELECT * FROM BLOG")  List<Blog> selectBlog();}

?

如果想创建自己的LanguageDriver的话,你需要实现LanguageDriver接口:

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

?

如果你确认自己的SQL仅作参数替换,而不需要Dynamic特性的话,就可以设定自己的SQL为raw,提升性能。

?

Dynamic SQL的特性就这么多,如果理解深刻、使用熟练的话,那在工作中是如虎添翼,不用考虑SQL拼接了。这个对于开发者来说,能够更好的专注于业务逻辑的开发,提升工作效率。?

本文就到这里。

  相关解决方案