前面幾篇博客咱們經過實例講解了用mybatis對一張表進行的CRUD操做,可是咱們發現寫的 SQL 語句都比較簡單,若是有比較複雜的業務,咱們須要寫複雜的 SQL 語句,每每須要拼接,而拼接 SQL ,稍微不注意,因爲引號,空格等缺失可能都會致使錯誤。java
那麼怎麼去解決這個問題呢?這就是本篇所講的使用 mybatis 動態SQL,經過 if, choose, when, otherwise, trim, where, set, foreach等標籤,可組合成很是靈活的SQL語句,從而在提升 SQL 語句的準確性的同時,也大大提升了開發人員的效率。sql
咱們以 User 表爲例來講明:session
一、動態SQL:if 語句
根據 username 和 sex 來查詢數據。若是username爲空,那麼將只根據sex來查詢;反之只根據username來查詢mybatis
首先不使用 動態SQL 來書寫app
1
2
3
4
5
6
|
<select id=
"selectUserByUsernameAndSex"
resultType=
"user"
parameterType=
"com.ys.po.User"
>
<!-- 這裏和普通的sql 查詢語句差很少,對於只有一個參數,後面的 #{id}表示佔位符,裏面不必定要寫id,
寫啥均可以,可是不要空着,若是有多個參數則必須寫pojo類裏面的屬性 -->
select * from user where username=#{username} and sex=#{sex}
</select>
|
上面的查詢語句,咱們能夠發現,若是 #{username} 爲空,那麼查詢結果也是空,如何解決這個問題呢?使用 if 來判斷ide
1
2
3
4
5
6
7
8
9
10
|
<select id=
"selectUserByUsernameAndSex"
resultType=
"user"
parameterType=
"com.ys.po.User"
>
select * from user where
<
if
test=
"username != null"
>
username=#{username}
</
if
>
<
if
test=
"username != null"
>
and sex=#{sex}
</
if
>
</select>
|
這樣寫咱們能夠看到,若是 sex 等於 null,那麼查詢語句爲 select * from user where username=#{username},可是若是usename 爲空呢?那麼查詢語句爲 select * from user where and sex=#{sex},這是錯誤的 SQL 語句,如何解決呢?請看下面的 where 語句post
二、動態SQL:if+where 語句
1
2
3
4
5
6
7
8
9
10
11
12
|
<select id=
"selectUserByUsernameAndSex"
resultType=
"user"
parameterType=
"com.ys.po.User"
>
select * from user
<where>
<
if
test=
"username != null"
>
username=#{username}
</
if
>
<
if
test=
"username != null"
>
and sex=#{sex}
</
if
>
</where>
</select>
|
這個「where」標籤會知道若是它包含的標籤中有返回值的話,它就插入一個‘where’。此外,若是標籤返回的內容是以AND 或OR 開頭的,則它會剔除掉。測試
三、動態SQL:if+set 語句
同理,上面的對於查詢 SQL 語句包含 where 關鍵字,若是在進行更新操做的時候,含有 set 關鍵詞,咱們怎麼處理呢?this
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
<!-- 根據 id 更新 user 表的數據 -->
<update id=
"updateUserById"
parameterType=
"com.ys.po.User"
>
update user u
<set>
<
if
test=
"username != null and username != ''"
>
u.username = #{username},
</
if
>
<
if
test=
"sex != null and sex != ''"
>
u.sex = #{sex}
</
if
>
</set>
where id=#{id}
</update>
|
這樣寫,若是第一個條件 username 爲空,那麼 sql 語句爲:update user u set u.sex=? where id=?
若是第一個條件不爲空,那麼 sql 語句爲:update user u set u.username = ? ,u.sex = ? where id=?
四、動態SQL:choose(when,otherwise) 語句
有時候,咱們不想用到全部的查詢條件,只想選擇其中的一個,查詢條件有一個知足便可,使用 choose 標籤能夠解決此類問題,相似於 Java 的 switch 語句
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
<select id=
"selectUserByChoose"
resultType=
"com.ys.po.User"
parameterType=
"com.ys.po.User"
>
select * from user
<where>
<choose>
<when test=
"id !='' and id != null"
>
id=#{id}
</when>
<when test=
"username !='' and username != null"
>
and username=#{username}
</when>
<otherwise>
and sex=#{sex}
</otherwise>
</choose>
</where>
</select>
|
也就是說,這裏咱們有三個條件,id,username,sex,只能選擇一個做爲查詢條件
若是 id 不爲空,那麼查詢語句爲:select * from user where id=?
若是 id 爲空,那麼看username 是否爲空,若是不爲空,那麼語句爲 select * from user where username=?;
若是 username 爲空,那麼查詢語句爲 select * from user where sex=?
五、動態SQL:trim 語句
trim標記是一個格式化的標記,能夠完成set或者是where標記的功能
①、用 trim 改寫上面第二點的 if+where 語句
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
<select id=
"selectUserByUsernameAndSex"
resultType=
"user"
parameterType=
"com.ys.po.User"
>
select * from user
<!-- <where>
<
if
test=
"username != null"
>
username=#{username}
</
if
>
<
if
test=
"username != null"
>
and sex=#{sex}
</
if
>
</where> -->
<trim prefix=
"where"
prefixOverrides=
"and | or"
>
<
if
test=
"username != null"
>
and username=#{username}
</
if
>
<
if
test=
"sex != null"
>
and sex=#{sex}
</
if
>
</trim>
</select>
|
prefix:前綴
prefixoverride:去掉第一個and或者是or
②、用 trim 改寫上面第三點的 if+set 語句
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
<!-- 根據 id 更新 user 表的數據 -->
<update id=
"updateUserById"
parameterType=
"com.ys.po.User"
>
update user u
<!-- <set>
<
if
test=
"username != null and username != ''"
>
u.username = #{username},
</
if
>
<
if
test=
"sex != null and sex != ''"
>
u.sex = #{sex}
</
if
>
</set> -->
<trim prefix=
"set"
suffixOverrides=
","
>
<
if
test=
"username != null and username != ''"
>
u.username = #{username},
</
if
>
<
if
test=
"sex != null and sex != ''"
>
u.sex = #{sex},
</
if
>
</trim>
where id=#{id}
</update>
|
suffix:後綴
suffixoverride:去掉最後一個逗號(也能夠是其餘的標記,就像是上面前綴中的and同樣)
六、動態SQL: SQL 片斷
有時候可能某個 sql 語句咱們用的特別多,爲了增長代碼的重用性,簡化代碼,咱們須要將這些代碼抽取出來,而後使用時直接調用。
好比:假如咱們須要常常根據用戶名和性別來進行聯合查詢,那麼咱們就把這個代碼抽取出來,以下:
1
2
3
4
5
6
7
8
9
|
<!-- 定義 sql 片斷 -->
<sql id=
"selectUserByUserNameAndSexSQL"
>
<
if
test=
"username != null and username != ''"
>
AND username = #{username}
</
if
>
<
if
test=
"sex != null and sex != ''"
>
AND sex = #{sex}
</
if
>
</sql>
|
引用 sql 片斷
1
2
3
4
5
6
7
8
|
<select id=
"selectUserByUsernameAndSex"
resultType=
"user"
parameterType=
"com.ys.po.User"
>
select * from user
<trim prefix=
"where"
prefixOverrides=
"and | or"
>
<!-- 引用 sql 片斷,若是refid 指定的不在本文件中,那麼須要在前面加上 namespace -->
<include refid=
"selectUserByUserNameAndSexSQL"
></include>
<!-- 在這裏還能夠引用其餘的 sql 片斷 -->
</trim>
</select>
|
注意:①、最好基於 單表來定義 sql 片斷,提升片斷的可重用性
②、在 sql 片斷中不要包括 where
七、動態SQL: foreach 語句
需求:咱們須要查詢 user 表中 id 分別爲1,2,3的用戶
sql語句:select * from user where id=1 or id=2 or id=3
select * from user where id in (1,2,3)
①、創建一個 UserVo 類,裏面封裝一個 List<Integer> ids 的屬性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
package
com.ys.vo;
import
java.util.List;
public
class
UserVo {
//封裝多個用戶的id
private
List<Integer> ids;
public
List<Integer> getIds() {
return
ids;
}
public
void
setIds(List<Integer> ids) {
this
.ids = ids;
}
}
|
②、咱們用 foreach 來改寫 select * from user where id=1 or id=2 or id=3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
<select id=
"selectUserByListId"
parameterType=
"com.ys.vo.UserVo"
resultType=
"com.ys.po.User"
>
select * from user
<where>
<!--
collection:指定輸入對象中的集合屬性
item:每次遍歷生成的對象
open:開始遍歷時的拼接字符串
close:結束時拼接的字符串
separator:遍歷對象之間須要拼接的字符串
select * from user where
1
=
1
and (id=
1
or id=
2
or id=
3
)
-->
<foreach collection=
"ids"
item=
"id"
open=
"and ("
close=
")"
separator=
"or"
>
id=#{id}
</foreach>
</where>
</select>
|
測試:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
//根據id集合查詢user表數據
@Test
public
void
testSelectUserByListId(){
String statement =
"com.ys.po.userMapper.selectUserByListId"
;
UserVo uv =
new
UserVo();
List<Integer> ids =
new
ArrayList<>();
ids.add(
1
);
ids.add(
2
);
ids.add(
3
);
uv.setIds(ids);
List<User> listUser = session.selectList(statement, uv);
for
(User u : listUser){
System.out.println(u);
}
session.close();
}
|
③、咱們用 foreach 來改寫 select * from user where id in (1,2,3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
<select id=
"selectUserByListId"
parameterType=
"com.ys.vo.UserVo"
resultType=
"com.ys.po.User"
>
select * from user
<where>
<!--
collection:指定輸入對象中的集合屬性
item:每次遍歷生成的對象
open:開始遍歷時的拼接字符串
close:結束時拼接的字符串
separator:遍歷對象之間須要拼接的字符串
select * from user where
1
=
1
and id in (
1
,
2
,
3
)
-->
<foreach collection=
"ids"
item=
"id"
open=
"and id in ("
close=
") "
separator=
","
>
#{id}
</foreach>
</where>
</select>
|
八、總結
其實動態 sql 語句的編寫每每就是一個拼接的問題,爲了保證拼接準確,咱們最好首先要寫原生的 sql 語句出來,而後在經過 mybatis 動態sql 對照着改,防止出錯。