其實MyBatis具有的一個強大的特性之一通常是它的動態 SQL 能力。 如果你有使用 JDBC 或其他 相似框架的經驗,你就明白要動態的串聯 SQL 字符串在一起是十分糾結的,確保不能忘了空格或在列表的最后省略逗號。Mybatis中的動態 SQL 可以徹底處理這種痛苦。對于動態SQL,最通俗簡單的方法就是我們自己在硬編碼的時候賦予各種動態行為的判斷,而在Mybatis中,用一種強大的動態 SQL 語 言來改進這種情形,這種語言可以被用在任意映射的 SQL 語句中。動態 SQL 元素和使用 JSTL 或其他相似的基于 XML 的文本處理器相似。MyBatis 采用功能強大的基于 OGNL 的表達式來消除其他元素。
我們常用的幾個節點元素有if,choose(when, otherwise),trim(where, if),foreach。真正使用下來我感覺有點像XSLT(文章后面會順帶提一下~)的用法。
(1)if 的用法
在ViisitMapper的分頁配置中,如果pageIndex>-1 and pageSize>-1的時候就加入相應的分頁SQL,否則就不添加(默認取全部),如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
< select id = "getListByPagenate" parameterType = "PagenateArgs" resultType = "Visitor" > select * from ( < include refid = "getListSql" /> < include refid = "orderBySql" /> ) t <!-- #{}表示參數化輸出,${}表示直接輸出不進行任何轉義操作,自己進行轉移 --> < if test="pageStart>-1 and pageSize>-1"> limit #{pageStart}, #{pageSize} </ if > </ select > < sql id = "getListSql" > select * from Visitor where status>0 </ sql > < sql id = "orderBySql" > order by ${orderFieldStr} ${orderDirectionStr} </ sql > |
因為我們的參數pageIndex與pageSize都是int值所以可以這樣直接判斷,如果是對象實例我們可以利用null判斷來進行一些動態邏輯的控制,具體實際開發中就要看業務需求了。這里我認為要注意的是別十分順手的吧and寫成&&,這個在配置中不會被識別~。
(2)choose (when, otherwise)的用法
choose when 主要在多個條件的情況下只滿足其中一個條件的應用場景中使用,例如這里就構建一個query條件,分別傳遞id,name與createTime。假設我們查詢Visitor表時,如果VisitorId有值則,使用Id查詢,如果VisitorName有值則采用VisitName查詢,如下,還是在david.mybatis.demo.IVisitorOperation接口類中添加List<Visitor> getListChooseWhenDemo(BasicQueryArgs args)方法。在VisitorMapper中添加相應的的select節點配置:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
|
package david.mybatis.demo; import java.util.List; import david.mybatis.model.BasicQueryArgs; import david.mybatis.model.PagenateArgs; import david.mybatis.model.Visitor; import david.mybatis.model.VisitorWithRn; public interface IVisitorOperation { /* * 添加訪問者 */ public int add(Visitor visitor); /* * 刪除訪問者 */ public int delete(int id); /* * 更新訪問者 */ public int update(Visitor visitor); /* * 查詢訪問者 */ public Visitor query(int id); /* * 查詢List */ public List<Visitor> getList(); /* * 分頁查詢List */ public List<Visitor> getListByPagenate(PagenateArgs args); /* * 分頁查詢List(包含Rownum) */ public List<VisitorWithRn> getListByPagenateWithRn(PagenateArgs args); /* * 基礎查詢 */ public Visitor basicQuery(int id); /* * 動態條件查詢(choose,when)實例 */ public List<Visitor> getListChooseWhenDemo(BasicQueryArgs args); /* * 動態條件查詢(where,if)實例 */ public List<Visitor> getListWhereDemo(BasicQueryArgs args); /* * 動態查詢(foreach)實例 */ public List<Visitor> getListForeachDemo(List<Integer> ids); } |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
<? xml version = "1.0" encoding = "UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> < mapper namespace = "david.mybatis.demo.IVisitorOperation" > < resultMap type = "Visitor" id = "visitorRs" > < id column = "Id" property = "id" /> < result column = "Name" property = "name" /> < result column = "Email" property = "email" /> < result column = "Status" property = "status" /> < result column = "CreateTime" property = "createTime" /> </ resultMap > < sql id = "getListSqlConditions" > select * from Visitor </ sql > <!-- 滿足其中一個條件時候用choose when操作 --> < select id = "getListChooseWhenDemo" resultMap = "visitorRs" parameterType = "BasicQueryArgs" > < include refid = "getListSqlConditions" /> < where > < if test="queryStatus>0"> status=#{queryStatus} </ if > < choose > < when test = "queryId!=0" > and id=#{queryId} </ when > < when test = "queryName!=null" > and name like #{queryName} </ when > < otherwise > and createTime>= #{queryTime} </ otherwise > </ choose > </ where > </ select > </ mapper > |
(3)where if (trim)的用法
where關鍵詞的好處是在于,如果有相應的過濾條件的話,它知道在適當的時候插入where關鍵詞。而且它也知道在何時該去掉相應的AND與OR的連接符,主要應對如下情景
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
< 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 > |
不會因為所有條件不滿足變為
1
2
3
4
5
|
< select id = "findActiveBlogLike" resultType = "Blog" > SELECT * FROM BLOG WHERE </ select > |
或者因為沒有滿足第一個條件,單單滿足后面的條件變成
1
2
3
4
5
6
|
< select id = "findActiveBlogLike" resultType = "Blog" > SELECT * FROM BLOG WHERE AND title like ‘someTitle' </ select > |
所以針對這種我們可以在建立choose when條件示例,同樣在IVisitorOperation接口類中加入相應的方法public List<Visitor> getListWhereDemo(BasicQueryArgs args),把VisitorMapper配置文件中的相對應配置加上去如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
<? xml version = "1.0" encoding = "UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> < mapper namespace = "david.mybatis.demo.IVisitorOperation" > < sql id = "getListSqlConditions" > select * from Visitor </ sql > <!-- 滿足條件的都加上去操作 --> < select id = "getListWhereDemo" resultMap = "visitorRs" parameterType = "BasicQueryArgs" > < include refid = "getListSqlConditions" /> < where > < if test="queryStatus>0"> status>0 </ if > < if test="queryId>0"> and id=#{queryId} </ if > < if test = "queryName!=null" > and name like=#{queryName} </ if > < if test = "queryTime!=null" > and createTime>=#{queryTime} </ if > </ where > <!-- <trim prefix="WHERE" prefixOverrides="AND |OR "> <if test="queryStatus>0"> status>0 </if> <if test="queryId>0"> and id=#{queryId} </if> <if test="queryName!=null"> and name like=#{queryName} </if> <if test="queryTime!=null"> and createTime>=#{queryTime} </if> </trim> --> </ select > </ mapper > |
(4)foreach的用法
在常用的動態SQL中我們有個業務場景是要where id in 一大串的ID,像這種情況我們就可以用到foreach啦,不必自己辛辛苦苦去拼接Id字符串啦。同樣的步驟還是在IVisitorOperation接口類中加入相應的方法public List<Visitor> getListForeachDemo(List<Integer> ids),然后再對應的Mapper文件中配置上相應的節點元素信息,如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
<? xml version = "1.0" encoding = "UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> < mapper namespace = "david.mybatis.demo.IVisitorOperation" > < sql id = "getListSqlConditions" > select * from Visitor </ sql > <!-- Foreach循環條件 --> < select id = "getListForeachDemo" resultMap = "visitorRs" > < include refid = "getListSqlConditions" /> where status>0 and id in < foreach collection = "list" item = "item" index = "index" open = "(" separator = "," close = ")" > ${item} </ foreach > </ select > </ mapper > |
最后你只需要在DemoRun中建立相應的測試方法,Mybatis里面的動態SQL也就完成啦,下面測試用的DemoRun方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
/* * 動態查詢foreach實例 */ public static void getListForeachDemo(List<Integer> ids) { SqlSession session = MybatisUtils.getSqlSession(); IVisitorOperation vOperation = session.getMapper(IVisitorOperation.class); List<Visitor> ls = vOperation.getListForeachDemo(ids); for (Visitor visitor : ls) { System.out.println(visitor); } } /* * 動態查詢where if實例 */ public static void getListWhereCondition(int id, String name, Date createTime) { name = name == "" ? null : name; SqlSession session = MybatisUtils.getSqlSession(); BasicQueryArgs args = new BasicQueryArgs(id, name, createTime); IVisitorOperation vOperation = session.getMapper(IVisitorOperation.class); List<Visitor> ls = vOperation.getListWhereDemo(args); if (ls.size() == 0) System.out.println("查無匹配!"); else { for (Visitor visitor : ls) { System.out.println(visitor); } } } /* * 動態查詢choose when實例 */ public static void getListChooseWhenDemo( int id, String name, Date createTime) { name = name == "" ? null : name; SqlSession session = MybatisUtils.getSqlSession(); BasicQueryArgs args = new BasicQueryArgs(id, name, createTime); IVisitorOperation vOperation = session.getMapper(IVisitorOperation. class ); List<Visitor> ls = vOperation.getListChooseWhenDemo(args); if (ls.size() == 0 ) System.out.println( "查無匹配!" ); else { for (Visitor visitor : ls) { System.out.println(visitor); } } } |
PS:關于OGNL
OGNL 是 Object-Graph Navigation Language 的縮寫,從語言角度來說:它是一個功能強大的表達式語言,用來獲取和設置 java 對象的屬性 , 它旨在提供一個更高抽象度語法來對 java 對象圖進行導航,OGNL 在許多的地方都有應用,例如:
作為 GUI 元素(textfield,combobox, 等)到模型對象的綁定語言。
數據庫表到 Swing 的 TableModel 的數據源語言。
web 組件和后臺 Model 對象的綁定語言 (WebOGNL,Tapestry,WebWork,WebObjects) 。
作為 Jakarata Commons BeanUtils 或者 JSTL 的表達式語言的一個更具表達力的替代語言。
另外,java 中很多可以做的事情,也可以使用 OGNL 來完成,例如:列表映射和選擇。 對于開發者來說,使用 OGNL,可以用簡潔的語法來完成對 java 對象的導航。通常來說: 通過一個“路徑”來完成對象信息的導航,這個“路徑”可以是到 java bean 的某個屬性,或者集合中的某個索引的對象,等等,而不是直接使用 get 或者 set 方法來完成。