reference: http://www.mybatis.org/mybatis-3/dynamic-sql.html

Dynamic SQL

One of the most powerful features of MyBatis has always been its Dynamic SQL capabilities. If you have any experience with JDBC or any similar framework, you understand how painful it is to conditionally concatenate strings of SQL together, making sure not to forget spaces or to omit a comma at the end of a list of columns. Dynamic SQL can be downright painful to deal with.

While working with Dynamic SQL will never be a party, MyBatis certainly improves the situation with a powerful Dynamic SQL language that can be used within any mapped SQL statement.

The Dynamic SQL elements should be familiar to anyone who has used JSTL or any similar XML based text processors. In previous versions of MyBatis, there were a lot of elements to know and understand. MyBatis 3 greatly improves upon this, and now there are less than half of those elements to work with. MyBatis employs powerful OGNL based expressions to eliminate most of the other elements:

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

if

The most common thing to do in dynamic SQL is conditionally include a part of a where clause. For example:

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

This statement would provide an optional text search type of functionality. If you passed in no title, then all active Blogs would be returned. But if you do pass in a title, it will look for a title like that (for the keen eyed, yes in this case your parameter value would need to include any masking or wildcard characters).

What if we wanted to optionally search by title and author? First, I’d change the name of the statement to make more sense. Then simply add another condition.

<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

Sometimes we don’t want all of the conditionals to apply, instead we want to choose only one case among many options. Similar to a switch statement in Java, MyBatis offers a choose element.

Let’s use the example above, but now let’s search only on title if one is provided, then only by author if one is provided. If neither is provided, let’s only return featured blogs (perhaps a strategically list selected by administrators, instead of returning a huge meaningless list of random blogs).

<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

The previous examples have been conveniently dancing around a notorious dynamic SQL challenge. Consider what would happen if we return to our "if" example, but this time we make "ACTIVE = 1" a dynamic condition as well.

<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>

What happens if none of the conditions are met? You would end up with SQL that looked like this:

SELECT * FROM BLOG
WHERE

This would fail. What if only the second condition was met? You would end up with SQL that looked like this:

SELECT * FROM BLOG
WHERE
AND title like ‘someTitle’

This would also fail. This problem is not easily solved with conditionals, and if you’ve ever had to write it, then you likely never want to do so again.

MyBatis has a simple answer that will likely work in 90% of the cases. And in cases where it doesn’t, you can customize it so that it does. With one simple change, everything works fine:

<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>

The where element knows to only insert "WHERE" if there is any content returned by the containing tags. Furthermore, if that content begins with "AND" or "OR", it knows to strip it off.

If the where element does not behave exactly as you like, you can customize it by defining your own trim element. For example, the trim equivalent to the where element is:

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

The prefixOverrides attribute takes a pipe delimited list of text to override, where whitespace is relevant. The result is the removal of anything specified in the prefixOverridesattribute, and the insertion of anything in the prefix attribute.

There is a similar solution for dynamic update statements called set. The set element can be used to dynamically include columns to update, and leave out others. For example:

<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>

Here, the set element will dynamically prepend the SET keyword, and also eliminate any extraneous commas that might trail the value assignments after the conditions are applied.

If you’re curious about what the equivalent trim element would look like, here it is:

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

Notice that in this case we’re overriding a suffix, while we’re still appending a prefix.

foreach

Another common necessity for dynamic SQL is the need to iterate over a collection, often to build an IN condition. For example:

<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>

The foreach element is very powerful, and allows you to specify a collection, declare item and index variables that can be used inside the body of the element. It also allows you to specify opening and closing strings, and add a separator to place in between iterations. The element is smart in that it won’t accidentally append extra separators.

NOTE You can pass any Iterable object (for example List, Set, etc.), as well as any Map or Array object to foreach as collection parameter. When using an Iterable or Array, index will be the number of current iteration and value item will be the element retrieved in this iteration. When using a Map (or Collection of Map.Entry objects), index will be the key object and item will be the value object.

This wraps up the discussion regarding the XML configuration file and XML mapping files. The next section will discuss the Java API in detail, so that you can get the most out of the mappings that you’ve created.

bind

The bind element lets you create a variable out of an OGNL expression and bind it to the context. For example:

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

Multi-db vendor support

If a databaseIdProvider was configured a "_databaseId" variable is available for dynamic code, so you can build different statements depending on database vendor. Have a look at the following example:

<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>

Pluggable Scripting Languages For Dynamic SQL

Starting from version 3.2 MyBatis supports pluggable scripting languages, so you can plug a language driver and use that language to write your dynamic SQL queries.

You can plug a language by implementing the following interface:

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);
}

Once you have your custom language driver you can set it to be the default by configuring it in the mybatis-config.xml file:

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

Instead of changing the default, you can specify the language for an specific statement by adding the lang attribute as follows:

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

Or, in the case you are using mappers, using the @Lang annotation:

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

NOTE You can use Apache Velocity as your dynamic language. Have a look at the MyBatis-Velocity project for the details.

All the xml tags you have seen in the previous sections are provided by the default MyBatis language that is provided by the driverorg.apache.ibatis.scripting.xmltags.XmlLanguageDriver which is aliased as xml.

mybatis Dynamic SQL的更多相关文章

  1. mybatis Dynamic SQL动态 SQL

    动态 SQL MyBatis 的强大特性之一便是它的动态 SQL.如果你有使用 JDBC 或其它类似框架的经验,你就能体会到根据不同条件拼接 SQL 语句的痛苦.例如拼接时要确保不能忘记添加必要的空格 ...

  2. MyBatis(3.2.3) - Dynamic SQL

    Sometimes, static SQL queries may not be sufficient for application requirements. We may have to bui ...

  3. Can I use MyBatis to generate Dynamic SQL without executing it?

    Although MyBatis was designed to execute the query after it builds it, you can make use of it's conf ...

  4. Mybatis 动态sql标签

    1.动态SQL片段 通过SQL片段达到代码复用         <!-- 动态条件分页查询 -->          <sql id="sql_count"> ...

  5. Mybatis Dynamic Query 框架整合

    项目地址:https://github.com/wz2cool/mybatis-dynamic-query 文档地址:https://wz2cool.gitbooks.io/mybatis-dynam ...

  6. Mybatis Dynamic Query 1.0.2版本

    项目地址:https://github.com/wz2cool/mybatis-dynamic-query 文档地址:https://wz2cool.gitbooks.io/mybatis-dynam ...

  7. Mybatis Dynamic Query 2.0.2

    项目地址:https://github.com/wz2cool/mybatis-dynamic-query 文档地址:https://wz2cool.gitbooks.io/mybatis-dynam ...

  8. mybatis-3 Dynamic SQL

    Dynamic SQL One of the most powerful features of MyBatis has always been its Dynamic SQL capabilitie ...

  9. Java EE数据持久化框架 • 【第4章 MyBatis动态SQL】

    全部章节   >>>> 本章目录 4.1 MyBatis动态标签 4.1.1  MyBatis动态标签介绍 4.1.2 < if >标签 4.1.3 update语 ...

随机推荐

  1. 一个故意消耗内存的java程序MemoryEater

    公司提供的测试服务器是vmware的,号称给我6G, 物理内存事实上是按需分配的. 等到真正拿到6G物理内存,黄花菜都凉了. 看到下面的文章,觉得故意用java程序侵占6G内存, 然后把侵占到内存的释 ...

  2. BeginInit与EndInit的实践总结

    在项目中,遇到这种情况,总结随便如下: 初始化时:添加操作,BeginInit{flag=true}  警情是一条条加入的,全部都加入后,图表再一次性生成   EndInit{flag=false} ...

  3. Hive之GROUP BY详解

    一,GROUP BY 执行理解 先来看下表1,表名为test: 表1 执行如下SQL语句: SELECT name from test GROUP BY name ;   你应该很容易知道运行的结果, ...

  4. 个人知识管理系统Version1.0开发记录(04)

    demo model 我们采用mvc软件架构模式,方便以后用Struts2框架技术优化.重构.封装.这次主要设计一些常用的方法工具,即数据访问逻辑.工具:eclipse.oracle.sqldevel ...

  5. OATable中column栏位数据居中的实现方法及代码

    // Table column中显示居中的实现 // QpriceResultTable1 为table的名称 // noPrice 为table中的列 OATableBean table = (OA ...

  6. 3321 Apple Tree 树状数组

    LIANJIE:http://poj.org/problem?id=3321 给你一个多叉树,每个叉和叶子节点有一颗苹果.然后给你两个操作,一个是给你C清除某节点上的苹果或者添加(此节点上有苹果则清除 ...

  7. less 全局变量使用

    less 全局变量使用 忽然想定义一个变量,但是却需要每个 .vue 文件都需要单独引入这个全局变量才可以,导致很多重复不必要的工作,因而得寻找一种可以任何地方都可以访问的方法 借助换肤这个功能 sa ...

  8. POJ 1321 棋盘问题 dfs 难度:0

    http://poj.org/problem?id=1321 注意是在'#'的地方放棋子 矩阵大小不过8*8,即使是8!的时间复杂度也足以承受,可以直接dfs求解 dfs时标注当前点的行和列已被访问, ...

  9. 分析hello.java文件

    使用JavaServer Faces技术的Web模块示例 1.hello1: hello1应用程序是一个web模块,它使用JavaServer Faces技术来显示问候和响应.可以使用文本编辑器查看应 ...

  10. POJ3177 Redundant Paths【tarjan边双联通分量】

    LINK 题目大意 给你一个有重边的无向图图,问你最少连接多少条边可以使得整个图双联通 思路 就是个边双的模板 注意判重边的时候只对父亲节点需要考虑 你就dfs的时候记录一下出现了多少条连向父亲的边就 ...