1. 程式人生 > 實用技巧 >7、MyBatis動態SQL

7、MyBatis動態SQL

學習資源:動力節點《2020最新MyBatis教程【IDEA版】-MyBatis從入門到精通》

目錄



動態 SQL,是指通過 MyBatis 提供的各種標籤對條件作出判斷以實現動態拼接 SQL 語句。 這裡的條件判
斷使用的表示式為 OGNL 表示式。 常用的動態 SQL 標籤有 <if>、<where>、<choose/>、<foreach> 等。

MyBatis 的動態 SQL 語句,與 JSTL 中的語句非常相似。

動態 SQL,主要用於解決查詢條件不確定的情況:在程式執行期間,根據使用者提交的查詢條件進行查詢。提交的查詢條件不同,執行的 SQL 語句不同。若將每種可能的情況均逐一列出,對所有條件進行排列組合,將會出現大量的 SQL 語句。此時,可使用動態 SQL 來解決這樣的問題。

使用動態 SQL ,需要 Java 物件作為方法引數。


在 mapper 的動態 SQL 中若出現大於號(>)、小於號(<)、大於等於號(>=),小於等於號(<=)等符號,最好將其轉換為實體符號。否則, XML 可能會出現解析出錯問題。特別是對於小於號(<),在 XML 中是絕不能出現的。否則解析 mapper 檔案會出錯。

實體符號表:

原始符號 實體符號
< &lt;
> &gt;
>= &gt;=
<= &lt;=

1、if

使用動態 SQL 最常見情景是根據條件包含 where 子句的一部分。

對於 <if> 標籤的執行,當 test 的值為 true 時,會將其包含的 SQL 片段拼接到其所在的 SQL 語句中。

語法: <if test="條件表示式"> sql 語句片段 </if>

使用例項:查詢名字為 #{name} 並且年齡大於15歲的學生的資訊,查詢的前提條件:傳入的引數 name 不為空,age>15。

  1. 介面方法:
List<Student> selectStudentIf(Student student);  
  1. 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 &gt; #{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的學生的資訊

  1. 介面方法
List<Student> selectStudentForList(List<Integer> idList);
  1. 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>
  1. 測試方法
@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的學生的資訊

  1. 介面方法
List<Student> selectStudentForList2(List<Student> stuList);
  1. 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>
  1. 測試方法
@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 片段使用步驟:

  1. 定義一個 SQL 片段
<sql id="自定義名稱,唯一性">  
    可複用的 sql語句, 表名,欄位等 
</sql>
  1. 其他 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 \&gt; #{age}
        </if>
    </where>
</select>