Sometimes, static SQL queries may not be sufficient for application requirements. We may have to build queries dynamically, based on some criteria.
For example, in web applications there could be search screens that provide one or more input options and perform searches based on the chosen criteria. While implementing this kind of search functionality, we may need to build a dynamic query based on the selected options. If the user provides any value for input criteria, we'll need to add that field in the WHERE clause of the query.

MyBatis provides first-class support for building dynamic SQL queries using elements such as <if>, <choose>, <where>, <foreach>, and <trim>.

The If condition

The <if> element can be used to conditionally embed SQL snippets. If the test condition is evaluated to true, then only the SQL snippet will be appended to the query.

Assume we have a Search Courses Screen that has a Tutor dropdown, the CourseName text field, and the StartDate and End Date input fields as the search criteria.

Assume that Tutor is a mandatory field and that the rest of the fields are optional.

When the user clicks on the search button, we need to display a list of courses that meet the following criteria:

  • Courses by the selected Tutor
  • Courses whose name contain the entered course name; if nothing has been provided, fetch all the courses
  • Courses whose start date and end date are in between the provided StartDate and EndDate input fields

We can create the mapped statement for searching the courses as follows:

  1. <resultMap type="Course" id="CourseResult">
  2. <id column="course_id" property="courseId"/>
  3. <result column="name" property="name"/>
  4. <result column="description" property="description"/>
  5. <result column="start_date" property="startDate"/>
  6. <result column="end_date" property="endDate"/>
  7. </resultMap>
  8. <select id="searchCourses" parameterType="hashmap" resultMap="CourseResult">
  9. <![CDATA[
  10. SELECT * FROM COURSES WHERE TUTOR_ID = #{tutorId}
  11. <if test="courseName != null">
  12. AND NAME LIKE #{courseName}
  13. </if>
  14. <if test="startDate != null">
  15. AND START_DATE >= #{startDate}
  16. </if>
  17. <if test="endDate != null">
  18. AND END_DATE <= #{endDate}
  19. </if>
  20. ]]>
  21. </select>
  1. public interface CourseMapper {
  2. List<Course> searchCourses(Map<String, Object> map);
  3. }
  4.  
  5. public void searchCourses() {
  6. Map<String, Object> map = new HashMap<String, Object>();
  7. map.put("tutorId", 1);
  8. map.put("courseName", "%java%");
  9. map.put("startDate", new Date());
  10. CourseMapper mapper = sqlSession.getMapper(CourseMapper.class);
  11. List<Course> courses = mapper.searchCourses(map);
  12. for (Course course : courses) {
  13. System.out.println(course);
  14. }
  15. }

This will generate the query SELECT * FROM COURSES WHERE TUTOR_ID= ? AND NAME like ? AND START_DATE >= ?. This will come in handy while preparing a dynamic SQL query based on the given criteria.

MyBatis uses OGNL (Object Graph Navigation Language) expressions for building dynamic queries.

The choose, when, and otherwise conditions

Sometimes, search functionality could be based on the search type. First, the user needs to choose whether he wants to search by Tutor or Course Name or Start Dates and End Dates, and then based on the selected search type, the input field will appear. In such scenarios, we should apply only one of the conditions. MyBatis provides the <choose> element to support this kind of dynamic SQL preparation.

Now let us write a SQL mapped statement to get the courses by applying the search criteria. If no search criteria is selected, the courses starting from today onwards should be fetched as follows:

  1. <select id="searchCourses" parameterType="hashmap" resultMap="CourseResult">
  2. SELECT * FROM COURSES
  3. <choose>
  4. <when test="searchBy == 'Tutor'">
  5. WHERE TUTOR_ID= #{tutorId}
  6. </when>
  7. <when test="searchBy == 'CourseName'">
  8. WHERE name like #{courseName}
  9. </when>
  10. <otherwise>
  11. WHERE TUTOR start_date &gt;= now()
  12. </otherwise>
  13. </choose>
  14. </select>

MyBatis evaluates the <choose> test conditions and uses the clause with the first condition that evaluates to TRUE. If none of the conditions are true, the <otherwise> clause will be used.

The where condition

At times, all the search criteria might be optional. In cases where at least one of the search conditions needs to be applied, then only the WHERE clause should be appended. Also, we need to append AND or OR to the conditions only if there are multiple conditions. MyBatis provides the <where> element to support building these kinds of dynamic SQL statements.

In the example Search Courses screen, we assume that all the search criteria is optional. So, the WHERE clause should be there only if any of the search criteria has been provided.

  1. <select id="searchCourses" parameterType="hashmap" resultMap="CourseResult">
  2. SELECT * FROM COURSES
  3. <where>
  4. <if test=" tutorId != null ">
  5. TUTOR_ID= #{tutorId}
  6. </if>
  7. <if test="courseName != null">
  8. AND name like #{courseName}
  9. </if>
  10. <if test="startDate != null">
  11. AND start_date &gt;= #{startDate}
  12. </if>
  13. <if test="endDate != null">
  14. AND end_date &lt;= #{endDate}
  15. </if>
  16. </where>
  17. </select>

The <where> element inserts WHERE only if any content is returned by the inner conditional tags. Also, it removes the AND or OR prefixes if the WHERE clause begins with AND or OR.

In the preceding example, if none of the <if> conditions are True, <where> won't insert the WHERE clause. If at least one of the <if> conditions is True, <where> will insert the WHERE clause followed by the content returned by the <if> tags.

If the tutor_id parameter is null and the courseName parameter is not null, <where> will take care of stripping out the AND prefix and adding NAME like #{courseName}.

The trim condition

The <trim> element works similar to <where> but provides additional flexibility on what prefix/suffix needs to be prefixed/suffixed and what prefix/suffix needs to be stripped off.

  1. <select id="searchCourses" parameterType="hashmap" resultMap="CourseResult">
  2. SELECT * FROM COURSES
  3. <trim prefix="WHERE" prefixOverrides="AND | OR">
  4. <if test="tutorId != null">
  5. TUTOR_ID= #{tutorId}
  6. </if>
  7. <if test="courseName != null">
  8. AND name like #{courseName}
  9. </if>
  10. </trim>
  11. </select>

Here <trim> will insert WHERE if any of the <if> conditions are true and remove the AND or OR prefixes just after WHERE.

The foreach loop

Another powerful dynamic SQL builder tag is <foreach>. It is a very common requirement for iterating through an array or list and for building AND/OR conditions or an IN clause.

Suppose we want to find out all the courses taught by the tutors whose tutor_id IDs are 1, 3, and 6. We can pass a list of tutor_id IDs to the mapped statement and build a dynamic query by iterating through the list using <foreach>.

  1. <select id="searchCoursesByTutors" parameterType="map" resultMap="CourseResult">
  2. SELECT * FROM COURSES
  3. <if test="tutorIds != null">
  4. <where>
  5. <foreach item="tutorId" collection="tutorIds">
  6. OR tutor_id=#{tutorId}
  7. </foreach>
  8. </where>
  9. </if>
  10. </select>
  1. public interface CourseMapper {
  2. List<Course> searchCoursesByTutors(Map<String, Object> map);
  3. }
  4.  
  5. public void searchCoursesByTutors() {
  6. Map<String, Object> map = new HashMap<String, Object>();
  7. List<Integer> tutorIds = new ArrayList<Integer>();
  8. tutorIds.add(1);
  9. tutorIds.add(3);
  10. tutorIds.add(6);
  11. map.put("tutorIds", tutorIds);
  12. CourseMapper mapper =
  13. sqlSession.getMapper(CourseMapper.class);
  14. List<Course> courses = mapper.searchCoursesByTutors(map);
  15. for (Course course : courses) {
  16. System.out.println(course);
  17. }
  18. }

Let us see how to use <foreach> to generate the IN clause:

  1. <select id="searchCoursesByTutors" parameterType="map" resultMap="CourseResult">
  2. SELECT * FROM COURSES
  3. <if test="tutorIds != null">
  4. <where>
  5. tutor_id IN
  6. <foreach item="tutorId" collection="tutorIds" open="(" separator="," close=")">
  7. #{tutorId}
  8. </foreach>
  9. </where>
  10. </if>
  11. </select>

The set condition

The <set> element is similar to the <where> element and will insert SET if any content is returned by the inner conditions.

  1. <update id="updateStudent" parameterType="Student">
  2. update students
  3. <set>
  4. <if test="name != null">name=#{name},</if>
  5. <if test="email != null">email=#{email},</if>
  6. <if test="phone != null">phone=#{phone},</if>
  7. </set>
  8. where stud_id=#{id}
  9. </update>

Here, <set> inserts the SET keyword if any of the <if> conditions return text and also strips out the tailing commas at the end.

In the preceding example, if phone != null, <set> will take care of removing the comma after phone=#{phone}.

MyBatis(3.2.3) - Dynamic SQL的更多相关文章

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

  2. mybatis Dynamic SQL

    reference: http://www.mybatis.org/mybatis-3/dynamic-sql.html Dynamic SQL One of the most powerful fe ...

  3. Spring mybatis源码篇章-NodeHandler实现类具体解析保存Dynamic sql节点信息

    前言:通过阅读源码对实现机制进行了解有利于陶冶情操,承接前文Spring mybatis源码篇章-XMLLanguageDriver解析sql包装为SqlSource SqlNode接口类 publi ...

  4. mybatis-3 Dynamic SQL

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

  5. Spring mybatis源码篇章-动态SQL基础语法以及原理

    通过阅读源码对实现机制进行了解有利于陶冶情操,承接前文Spring mybatis源码篇章-Mybatis的XML文件加载 前话 前文通过Spring中配置mapperLocations属性来进行对m ...

  6. Mybatis基于接口注解配置SQL映射器(二)

    Mybatis之增强型注解 MyBatis提供了简单的Java注解,使得我们可以不配置XML格式的Mapper文件,也能方便的编写简单的数据库操作代码.但是注解对动态SQL的支持一直差强人意,即使My ...

  7. [转]Dynamic SQL & Stored Procedure Usage in T-SQL

    转自:http://www.sqlusa.com/bestpractices/training/scripts/dynamicsql/ Dynamic SQL & Stored Procedu ...

  8. MyBatis学习总结_11_MyBatis动态Sql语句

    MyBatis中对数据库的操作,有时要带一些条件,因此动态SQL语句非常有必要,下面就主要来讲讲几个常用的动态SQL语句的语法 MyBatis中用于实现动态SQL的元素主要有: if choose(w ...

  9. MyBatis学习 之 二、SQL语句映射文件(2)增删改查、参数、缓存

    目录(?)[-] 二SQL语句映射文件2增删改查参数缓存 select insert updatedelete sql parameters 基本类型参数 Java实体类型参数 Map参数 多参数的实 ...

随机推荐

  1. HDU 4438 Hunters (数学,概率计算)

    题意:猎人A和B要进行一场比赛.现在有两个猎物老虎和狼,打死老虎可以得X分,打死狼可以得Y分.现在有两种情况: (1)如果A与B的预定目标不同,那么他们都将猎到预定的目标. (2)如果A与B的预定目标 ...

  2. dao 获取表最大排序实现

    public Long getMaxOrder(Long parentId) { Query query = this.getSession().createSQLQuery( "selec ...

  3. SAE搭建WordPress教程 免费建WordPress博客站

    SAE搭建WordPress教程 免费建WordPress博客站 WordPress是一种使用PHP语言开发的博客平台,用户可以在支持PHP和MySQL数据库的服务器上架设自己的网志.当然,用户也可以 ...

  4. 算法之旅,直奔<algorithm>之十七 find_first_of

    find_first_of(vs2010) 引言 这是我学习总结 <algorithm>的第十七篇,find_first_of是匹配的一个函数.<algorithm>是c++的 ...

  5. 文件频繁IO能有多大的差别

    测试文件写同样大小的文件,单次记录较小和单次记录较大能有多大的性能差别. 最终写入同样大小的文件,小记录需要写入10w次,大记录需要写入1w次,看下最终的性能报告

  6. Bootstrap 列偏移\列嵌套\列排序

    1.列偏移 这个其实很简单就是通过一个样式类,通过.col-md-offset-*可以将列偏移到右侧.这些class通过使用*选择器将所有列增加了列的左侧margin.例如,.col-md-offse ...

  7. 图片攻击-BMP图片中注入恶意JS代码 <转载>

    昨天看到一篇文章<hacking throung images>,里面介绍了如何在BMP格式的图片里注入JS代码,使得BMP图片既可以正常显示, 也可以运行其中的JS代码,觉得相当有趣. ...

  8. Struts 2.x异常:Unable to load configuration..../WEB-INF/lib/struts2-convention-plugin-2.1.6.jar!/struts-plugin.xml:30:119

    Struts 2.x异常:Unable to load configuration..../WEB-INF/lib/struts2-convention-plugin-2.1.6.jar!/strut ...

  9. TChromeTabs 使用日记

    1.如何让 Tab 在拖放时,拖放图形中带有 TabControl 的内容. 增加 ChromeTabs 的 NeedDragImageControl 事件,并在代码中设置 DragControl 为 ...

  10. Crystal Reports课程01-连接SQL Sever数据库

    选择[OLE DB(ADO)] 选择[microsoft DB provider for SQL Sever],点击[下一步] 填写连接的服务器,数据库,用户名,密码等信息,然后点击[下一步] 选择[ ...