1. 程式人生 > >MyBatis的學習(四)——MyBatis動態SQL

MyBatis的學習(四)——MyBatis動態SQL

一、動態SQL介紹

MyBatis 的強大特性之一便是它的動態 SQL。如果有使用 JDBC 或其他類似框架的經驗,就能體會到根據不同條件拼接 SQL 語句有多麼痛苦。拼接的時候要確保不能忘了必要的空格,還要注意省掉列名列表最後的逗號。利用動態 SQL 這一特性可以徹底擺脫這種痛苦。

通常使用動態 SQL 不可能是獨立的一部分,MyBatis 當然使用一種強大的動態 SQL 語言來改進這種情形,這種語言可以被用在任意的 SQL 對映語句中。

         動態 SQL 元素和使用 JSTL 或其他類似基於 XML 的文字處理器相似。在 MyBatis 之前的版本中,有很多的元素需要來了解。MyBatis 3 大大提升了它們,現在用不到原先一半的元素就可以了。MyBatis 採用功能強大的基於 OGNL

Struts2語法) 的表示式來消除其他元素。

         mybatis 的動態sql語句是基於OGNL表示式的。可以方便的在 sql 語句中實現某些邏輯. 總體說來mybatis 動態SQL 語句主要有以下幾類:

  1. if 語句 (簡單的條件判斷)
  2. choose (when,otherwise) ,相當於java 語言中的 switch ,與 jstl 中的choose 很類似.
  3. trim (對包含的內容加上 prefix,或者 suffix 等,字首,字尾)
  4. where (主要是用來簡化sql語句中where條件判斷的,能智慧的處理 and or ,不必擔心多餘導致語法錯誤)
  5. set (主要用於更新時)
  6. foreach (在實現 mybatis in 語句查詢時特別有用)

二、動態SQL的使用

首先建立資料庫和建立users表

建立users實體類:

package com.little.entity;

public class Users {
    private Integer id;
    private String name;
    private String sex;
    private String address;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "Users{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", sex='" + sex + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}

①if+where元素

根據name和sex元素來查詢資料,如何name為空,sex不為空,根據sex來查詢,反之根據name來查詢,都為空就全查,都不為空就根據兩者的條件來查詢。

<select id="queryByNameAndSex" resultType="Users" parameterType="Users">
        select * from users
        <where>
            <if test="name != null">
                and name = #{name}
            </if>
            <if test="sex != null">
                and sex = #{sex}
            </if>
        </where>
    </select>

這個“where”標籤會知道如果它包含的標籤中有返回值的話,它就插入一個‘where’。此外,如果標籤返回的內容是以AND 或OR 開頭的,則它會剔除掉。

②if+set語句

比如在進行更新的時候,使用到set語句,MyBatis也有對應的set元素

<update id="updateUsersById" parameterType="Users">
		update users u
		<set>
			<if test="name != null and name != ''">
				u.name = #{name},
			</if>
			<if test="sex != null and sex != ''">
				u.sex = #{sex},
			</if>
		</set>
		where id=#{id}
	</update>

如果出現多餘逗號 會自動去掉

③choose(when,otherwise)

有時候,我們不想用到所有的查詢條件,只想選擇其中的一個,查詢條件有一個滿足即可,使用 choose 標籤可以解決此類問題,類似於 Java 的 switch 語句

<select id="selectUsersByChoose" resultType="Users" parameterType="Users">
		select * from users
		<where>
			<choose>
				<when test="id !='' and id != null">
					id=#{id}
				</when>
				<when test="name !='' and name != null">
					and name=#{name}
				</when>
				<otherwise>
					and sex=#{sex}
				</otherwise>
			</choose>
		</where>
	</select>

這裡就是隻根據一個條件來進行查詢,按照順序進行

④trim

trim標記是一個格式化的標記,可以完成set或者是where標記的功能

用 trim 改寫上面的 if+where 語句

<select id="selectUsersByNameAndSex" resultType="users" parameterType="Users">
		select * from users
		<trim prefix="where" prefixOverrides="and | or">
			<if test="name != null">
				and name=#{name}
			</if>
			<if test="sex != null">
				and sex=#{sex}
			</if>
		</trim>
	</select>

prefix:字首      

prefixoverride:去掉第一個and或者是or

用 trim 改寫上面的 if+set 語句

<!-- 根據 id 更新 user 表的資料 -->
	<update id="updateUsersById" parameterType="Users">
		update users u
		<trim prefix="set" suffixOverrides=",">
			<if test="name != null and name != ''">
				u.name = #{name},
			</if>
			<if test="sex != null and sex != ''">
				u.sex = #{sex},
			</if>
		</trim>
		where id=#{id}
	</update>

suffix:字尾  

 suffixoverride:去掉最後一個逗號(也可以是其他的標記,就像是上面字首中的and一樣)

⑤SQL片段

有時候可能某個 sql 語句我們用的特別多,為了增加程式碼的重用性,簡化程式碼,我們需要將這些程式碼抽取出來,然後使用時直接呼叫。

比如:假如我們需要經常根據使用者名稱和性別來進行聯合查詢,那麼我們就把這個程式碼抽取出來,如下:

<!-- 定義 sql 片段 -->
	<sql id="selectUsersByNameAndSexSQL">
		<if test="name != null and name != ''">
			AND name = #{name}
		</if>
		<if test="sex != null and sex != ''">
			AND sex = #{sex}
		</if>
	</sql>

引用SQL片段

<select id="selectUsersByNameAndSex" resultType="users"
		parameterType="Users">
		select * from users
		<trim prefix="where" prefixOverrides="and | or">
			<!-- 引用 sql 片段,如果refid 指定的不在本檔案中,那麼需要在前面加上 namespace -->
			<include refid="selectUsersByNameAndSexSQL"></include>
			<!-- 在這裡還可以引用其他的 sql 片段 -->
		</trim>
	</select>

注意:最好基於 單表來定義 sql 片段,提高片段的可重用性

    sql 片段中不要包括 where

⑥foreach

需求:我們需要查詢 users 表中 id 分別為1,2,3的使用者

sql語句:select * from users where id=1 or id=2 or id=3 select * from users where id in (1,2,3)

建立一個 UserVo 類,裡面封裝一個 List<Integer> ids 的屬性

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 users where id=1 or id=2 or id=3

<select id="selectUsersByListId" parameterType="UserVo"
		resultType="Users">
		select * from users
		<where>
			<!-- collection:指定輸入物件中的集合屬性 item:每次遍歷生成的物件 open:開始遍歷時的拼接字串 close:結束時拼接的字串 
				separator:遍歷物件之間需要拼接的字串 select * from users 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>

測試:

//根據id集合查詢users表資料
	@Test
	public void testSelectUsersByListId(){
	    UserVo uv = new UserVo();
	    List<Integer> ids = new ArrayList<Integer>();
	    ids.add(1);
	    ids.add(2);
	    ids.add(3);
	    uv.setIds(ids);
	    List<Users> listUsers = mapper.selectUsersByListId(uv);
	    for(Users u : listUsers){
	        System.out.println(u);
	    }
	    session.close();
	}

我們用 foreach 來改寫 select * from users where id in (1,2,3)

<select id="selectUsersByListId" parameterType="Users" resultType="Users">
        select * from users
        <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>

單引數List的型別

<select id="dynamicForeachTest" resultType="Users">
		select * from users where id in
		<foreach collection="list" index="index" item="item" open="("
			separator="," close=")">
			#{item}
		</foreach>
	</select>

測試:

 public List<User> dynamicForeachTest(List<Integer> ids);


	@Test
	public void testSelectUserByListId1(){
	    List<Integer> ids = new ArrayList<Integer>();
	    ids.add(1);
	    ids.add(2);
	    ids.add(3);
	    List<Users> listUsers = mapper.dynamicForeachTest(ids);
	    for(Users u : listUsers){
	        System.out.println(u);
	    }
	}

單引數array陣列的型別

<select id="dynamicForeach2Test" resultType="User">
		select * from users where id in
		<foreach collection="array" index="index" item="item" open="("
			separator="," close=")">
			#{item}
		</foreach>
	</select>
public List<Users> dynamicForeach2Test(Integer[] ids);
	@Test
	public void dynamicForeach2Test(){
	    Integer[] ids={1,2,3};
		List<Users> listUsers = mapper.dynamicForeach2Test(ids);
	    for(Users u : listUsers){
	        System.out.println(u);
	    }
	}

將引數封裝成Map的型別

<select id="dynamicForeach3Test" resultType="User">
		select * from users where name like "%"#{name}"%" and id in
		<foreach collection="ids" index="index" item="item" open="("
			separator="," close=")">
			#{item}
		</foreach>
	</select>
public List<Users> dynamicForeach3Test(Map params);

測試:

@Test
	public void dynamicForeach3Test() {
		List ids = new ArrayList();
		ids.add(28);
		ids.add(29);
		ids.add(30);
		
		Map params = new HashMap();
		params.put("ids", ids);
		params.put("ame", "張");
		
		List<Users> listUsers = mapper.dynamicForeach3Test(params);
		for (Users u : listUsers) {
			System.out.println(u);
		}
	}