7、MyBatis動態SQL
目錄
動態 SQL,是指通過 MyBatis 提供的各種標籤對條件作出判斷以實現動態拼接 SQL 語句。 這裡的條件判
斷使用的表示式為 OGNL 表示式。 常用的動態 SQL 標籤有 <if>、<where>、<choose/>、<foreach> 等。
MyBatis 的動態 SQL 語句,與 JSTL 中的語句非常相似。
動態 SQL,主要用於解決查詢條件不確定的情況:在程式執行期間,根據使用者提交的查詢條件進行查詢。提交的查詢條件不同,執行的 SQL 語句不同。若將每種可能的情況均逐一列出,對所有條件進行排列組合,將會出現大量的 SQL 語句。此時,可使用動態 SQL 來解決這樣的問題。
使用動態 SQL ,需要 Java 物件作為方法引數。
在 mapper 的動態 SQL 中若出現大於號(>)、小於號(<)、大於等於號(>=),小於等於號(<=)等符號,最好將其轉換為實體符號。否則, XML 可能會出現解析出錯問題。特別是對於小於號(<),在 XML 中是絕不能出現的。否則解析 mapper 檔案會出錯。
實體符號表:
原始符號 | 實體符號 |
---|---|
< | < |
> | > |
>= | >= |
<= | <= |
1、if
使用動態 SQL 最常見情景是根據條件包含 where 子句的一部分。
對於 <if> 標籤的執行,當 test 的值為 true 時,會將其包含的 SQL 片段拼接到其所在的 SQL 語句中。
語法: <if test="條件表示式"> sql 語句片段 </if>
。
使用例項:查詢名字為 #{name} 並且年齡大於15歲的學生的資訊,查詢的前提條件:傳入的引數 name 不為空,age>15。
- 介面方法:
List<Student> selectStudentIf(Student student);
- mapper 檔案
<select id="selectStudentIf" resultType="com.bjpowernode.domain.Student">
select id,name,email,age from student
<!-- 小技巧 --->
where 1=1
<!-- <if:test="是使用方法引數 Java 物件的屬性值作為判斷條件"> -->
<!-- #{} 也是使用方法引數 Java 物件的·屬性值作為 sql 語句的引數 -->
<if test="name != null and name !='' ">
and name = #{name}
</if>
<if test="age > 15 ">
or age > #{age}
</if>
</select>
3、測試方法
@Test
public void test(){
SqlSession sqlSession = MyBatisUtil.getSqlSession();
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
// 傳入的物件是任意型別的,只要符合業務需求即可,此處使用 Student 的物件是因為懶
Student params = new Student();
params.setName("李四");
params.setAge(15);
List<Student> students = studentDao.selectStudentsIf(student);
students.forEach(System.out::println);
}
<if/> 標籤的中存在一個比較麻煩的地方:需要在 where 後手工新增 1=1 的子句。因為,若 where 後的所有 <if/> 條件均為 false,而 where 後若又沒有 1=1 子句,則 SQL 中就會只剩下一個空的 where,則SQL 出錯。所以,在 where 後,需要新增永為真的子句 1=1,以防止這種情況的發生。但當資料量很大時,會嚴重影響查詢效率。
2、where
如果使用 <where/> 標籤, 在其後有查詢條件時,可以自動新增上 where 子句;沒有查詢條件時,則不會新增 where 子句。
注意:第一個 <if/> 標籤中的 SQL 片斷,可寫可不寫 and 或 or,因為 MyBatis 會將多餘的 and 或 or 去掉;但之後的 <if/> 中 SQL 片斷的 and 或 or,必須要求寫上,否則 SQL 語句將拼接出錯。
<select id="findActiveBlogLike" resultType="Blog">
SELECT * FROM BLOG
<where>
<if test="state != null">
<!-- 此處也可以寫 and 或 or -->
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>
3、foreach
<foreach/> 標籤用於實現對於方法引數是陣列或集合中的元素的遍歷,主要用在 sql 的 in 語句中。
比如說,SQL中查詢 id 是1001、1002、1003的學生的資訊:
select * from student where id in (1001,1002,1003);
很容易想到,我們可以將這三個資料新增到一個集合中,將這個集合作為方法引數傳給 mapper 檔案,再從集合中遍歷元素拼接為 完整的 sql 語句。
現在先來模擬一下 MyBatis 對於該語句的拼接:
@Test
public void testfor(){
List<Integer> list = new ArrayList<>();
list.add(1001);
list.add(1002);
list.add(1003);
//String sql="select * from student where id in (1001,1002,1003)";
String sql="select * from student where id in";
StringBuilder builder = new StringBuilder("");
//迴圈開始,字串尾追加 (
builder.append("(");
for(Integer i:list){
// 字串尾追加 元素,
builder.append(i).append(",");
}
builder.deleteCharAt(builder.length()-1);
//迴圈結束,新增 )
builder.append(")");
sql = sql + builder.toString();
System.out.println("sql=="+sql);
}
<foreach> 的規範語法:
<foreach collection="" item="自定義名稱" open="" close="" separator="">
#{item的自定義名稱}
</foreach>
屬性介紹:
- collection:表示介面方法引數的型別,也即是表示要遍歷的集合的型別
- 陣列,賦值為 array
- 集合,賦值為 list
- item:表示當前遍歷到的集合成員,自定義名稱
- open:表示迴圈開始的字元
- close:表示迴圈結束的字元
- separator:集合成員之間的分隔符
由之前的模擬,就可以很容易得出各個成員變數的值:
- collection="list" 或 collection="array"
- item="自定義名稱"
- open="("
- close=")"
- seperator=","
3.1、遍歷 List<簡單型別>
使用例項:查詢 id 是 1002,1005,1006的學生的資訊
- 介面方法
List<Student> selectStudentForList(List<Integer> idList);
- mapper 檔案
<select id="selectStudentForList" resultType="com.bjpowernode.domain.Student">
select id,name,email,age from student
<if test="list !=null and list.size > 0 ">
where id in
<foreach collection="list" open="(" close=")" item="stuid" separator=",">
<!-- 遍歷簡單型別,佔位符使用item的值即可 -->
#{stuid}
</foreach>
</if>
</select>
- 測試方法
@Test
public void testSelectForList() {
List<Integer> list = new ArrayList<>();
list.add(1002);
list.add(1005);
list.add(1006);
List<Student> studentList = studentDao.selectStudentForList(list);
studentList.forEach( stu -> System.out.println(stu));
}
3.2、遍歷 List<物件型別>
使用例項:查詢 id 是 1002,1005,1006的學生的資訊
- 介面方法
List<Student> selectStudentForList2(List<Student> stuList);
- mapper 檔案
<select id="selectStudentForList2" resultType="com.bjpowernode.domain.Student">
select id,name,email,age from student
<if test="list !=null and list.size > 0 ">
where id in
<foreach collection="list" open="(" close=")" item="stuobject" separator=",">
#{stuobject.id}
</foreach>
</if>
</select>
- 測試方法
@Test
public void testSelectForList2() {
List<Student> list = new ArrayList<>();
Student stu = new Student();
stu.setId(1002);
list.add(stu);
stu = new Student();
stu.setId(1005);
stu = new Student();
stu.setId(1006);
list.add(stu);
List<Student> studentList = studentDao.selectStudentForList2(list);
studentList.forEach( stu -> System.out.println(stu));
}
4、choose、when、otherwise標籤
5、set
6、trim
7、SQL片段
<sql/> 標籤用於定義 SQL 片段,以便其它 SQL 標籤複用。
如果其它 SQL 標籤需要複用 SQL 片段,只需要使用 <include/> 子標籤進行引用即可。
SQL 片段使用步驟:
- 定義一個 SQL 片段
<sql id="自定義名稱,唯一性">
可複用的 sql語句, 表名,欄位等
</sql>
- 其他 SQL 標籤使用 <include/> 進行引用
<select id="" resultType="">
<include refid="sql標籤id的值" />
剩餘的sql語句部分
</select>
使用例項:
<sql id="select*">
select id, name, age, email, from student
</sql>
<select id="selectStudentsIf" resultType="com.bjpowernode.domin,Student">
<include refid="select*" />
<where>
<if test="name != null and name!='' ">
name=#{name}
</if>
<if test="age > 18">
or age \> #{age}
</if>
</where>
</select>