MyBatis從入門到精通(七):MyBatis動態Sql之choose,where,set標籤的用法

申城異鄉人發表於2019-07-09

最近在讀劉增輝老師所著的《MyBatis從入門到精通》一書,很有收穫,於是將自己學習的過程以部落格形式輸出,如有錯誤,歡迎指正,如幫助到你,不勝榮幸!

本篇部落格主要講解如何使用choose,where,set標籤生成動態的Sql。

1. choose 用法

假設有這樣1個需求:當引數id有值時優先使用id查詢,當id沒有值時就去判斷使用者名稱是否有值,如果有值就用使用者名稱查詢,如果沒值,就使查詢無結果。

首先,我們在介面SysUserMapper中新增如下方法:

/**
 * 根據使用者id或使用者名稱查詢
 *
 * @param sysUser
 * @return
 */
SysUser selectByIdOrUserName(SysUser sysUser);

然後在對應的SysUserMapper.xml中新增如下程式碼:

<select id="selectByIdOrUserName" resultType="com.zwwhnly.mybatisaction.model.SysUser">
    SELECT  id,
            user_name,
            user_password,
            user_email,
            create_time
    FROM sys_user
    WHERE 1 = 1
    <choose>
        <when test="id != null">
            AND id = #{id}
        </when>
        <when test="userName != null and userName != ''">
            AND user_name = #{userName}
        </when>
        <otherwise>
            AND 1 = 2
        </otherwise>
    </choose>
</select>

注意事項:

在以上的程式碼中,如果沒有otherwise這個限制條件,當id和userName都為null時,所有的使用者都會被查詢出來,但我們介面的返回值是SysUser,當查詢結果是多個時就會報錯。新增otherwise條件後,由於where條件不滿足,因此在這種情況下就查詢不到結果。

最後,在SysUserMapperTest測試類中新增如下測試方法:

@Test
public void testSelectByIdOrUserName() {
    SqlSession sqlSession = getSqlSession();

    try {
        SysUserMapper sysUserMapper = sqlSession.getMapper(SysUserMapper.class);

        // 按id查詢
        SysUser query = new SysUser();
        query.setId(1L);
        query.setUserName("admin");
        SysUser sysUser = sysUserMapper.selectByIdOrUserName(query);
        Assert.assertNotNull(sysUser);

        // 只按userName查詢
        query.setId(null);
        sysUser = sysUserMapper.selectByIdOrUserName(query);
        Assert.assertNotNull(sysUser);

        // id 和 userName 都為空
        query.setUserName(null);
        sysUser = sysUserMapper.selectByIdOrUserName(query);
        Assert.assertNull(sysUser);
    } finally {
        sqlSession.close();
    }
}

執行測試程式碼,測試通過,輸出日誌如下:

DEBUG [main] - ==> Preparing: SELECT id, user_name, user_password, user_email, create_time FROM sys_user WHERE 1 = 1 AND id = ?

DEBUG [main] - ==> Parameters: 1(Long)

TRACE [main] - <== Columns: id, user_name, user_password, user_email, create_time

TRACE [main] - <== Row: 1, admin, 123456, admin@mybatis.tk, 2019-06-27 18:21:07.0

DEBUG [main] - <== Total: 1

DEBUG [main] - ==> Preparing: SELECT id, user_name, user_password, user_email, create_time FROM sys_user WHERE 1 = 1 AND user_name = ?

DEBUG [main] - ==> Parameters: admin(String)

TRACE [main] - <== Columns: id, user_name, user_password, user_email, create_time

TRACE [main] - <== Row: 1, admin, 123456, admin@mybatis.tk, 2019-06-27 18:21:07.0

DEBUG [main] - <== Total: 1

DEBUG [main] - ==> Preparing: SELECT id, user_name, user_password, user_email, create_time FROM sys_user WHERE 1 = 1 AND 1 = 2

DEBUG [main] - ==> Parameters:

DEBUG [main] - <== Total: 0

2. where 用法

where標籤的作用:如果該標籤包含的元素中有返回值,就插入一個where,如果where後面的字串是以AND或者OR開頭的,就將它們剔除。

假設有這樣1個需求:根據使用者的輸入條件來查詢使用者列表,如果輸入了使用者名稱,就根據使用者名稱模糊查詢,如果輸入了郵箱,就根據郵箱精確查詢,如果同時輸入了使用者名稱和郵箱,就用這兩個條件去匹配使用者。

首先,我們在介面SysUserMapper中新增如下方法:

/**
 * 根據動態條件查詢使用者資訊(使用Where標籤)
 *
 * @param sysUser
 * @return
 */
List<SysUser> selectByUserWhere(SysUser sysUser);

然後在對應的SysUserMapper.xml中新增如下程式碼:

<select id="selectByUserWhere" resultType="com.zwwhnly.mybatisaction.model.SysUser">
    SELECT id,
    user_name,
    user_password,
    user_email,
    create_time
    FROM sys_user
    <where>
        <if test="userName != null and userName != ''">
            AND user_name LIKE CONCAT('%',#{userName},'%')
        </if>
        <if test="userEmail != null and userEmail != ''">
            AND user_email = #{userEmail}
        </if>
    </where>
</select>

當if條件都不滿足的時候,where元素中沒有內容,此時的Sql語句不會有where,語法正確。

如果if條件滿足,where元素的內容就是以AND開頭的條件,where會自動去掉開頭的and,保證Sql語句的正確。

最後,在SysUserMapperTest測試類中新增如下測試方法:

@Test
public void testSelectByUserWhere() {
    SqlSession sqlSession = getSqlSession();

    try {
        SysUserMapper sysUserMapper = sqlSession.getMapper(SysUserMapper.class);

        // 只按使用者名稱查詢
        SysUser query = new SysUser();
        query.setUserName("ad");
        List<SysUser> sysUserList = sysUserMapper.selectByUserWhere(query);
        Assert.assertTrue(sysUserList.size() > 0);

        // 只按郵箱查詢
        query = new SysUser();
        query.setUserEmail("test@mybatis.tk");
        sysUserList = sysUserMapper.selectByUserWhere(query);
        Assert.assertTrue(sysUserList.size() > 0);

        // 同時按使用者民和郵箱查詢
        query = new SysUser();
        query.setUserName("ad");
        query.setUserEmail("test@mybatis.tk");
        sysUserList = sysUserMapper.selectByUserWhere(query);
        // 由於沒有同時符合這兩個條件的使用者,因此查詢結果數為0
        Assert.assertTrue(sysUserList.size() == 0);
    } finally {
        sqlSession.close();
    }
}

執行測試程式碼,測試通過,輸出日誌如下:

DEBUG [main] - ==> Preparing: SELECT id, user_name, user_password, user_email, create_time FROM sys_user WHERE user_name LIKE CONCAT('%',?,'%')

DEBUG [main] - ==> Parameters: ad(String)

TRACE [main] - <== Columns: id, user_name, user_password, user_email, create_time

TRACE [main] - <== Row: 1, admin, 123456, admin@mybatis.tk, 2019-06-27 18:21:07.0

DEBUG [main] - <== Total: 1

DEBUG [main] - ==> Preparing: SELECT id, user_name, user_password, user_email, create_time FROM sys_user WHERE user_email = ?

DEBUG [main] - ==> Parameters: test@mybatis.tk(String)

TRACE [main] - <== Columns: id, user_name, user_password, user_email, create_time

TRACE [main] - <== Row: 1001, test, 123456, test@mybatis.tk, 2019-06-27 18:21:07.0

DEBUG [main] - <== Total: 1

DEBUG [main] - ==> Preparing: SELECT id, user_name, user_password, user_email, create_time FROM sys_user WHERE user_name LIKE CONCAT('%',?,'%') AND user_email = ?

DEBUG [main] - ==> Parameters: ad(String), test@mybatis.tk(String)

DEBUG [main] - <== Total: 0

3. set 用法

set標籤的作用:如果該標籤包含的元素中有返回值,就插入一個set,如果set後面的字串是以,結尾的,就將這個逗號剔除。

假設有這樣1個需求:更新使用者資訊的時候不能將原來有值但沒有發生變化的欄位更新為空或null,即只更新有值的欄位。

首先,我們在介面SysUserMapper中新增如下方法:

/**
 * 根據主鍵選擇性更新使用者資訊(使用Set標籤)
 *
 * @param sysUser
 * @return
 */
int updateByIdSelectiveSet(SysUser sysUser);

然後在對應的SysUserMapper.xml中新增如下程式碼:

<update id="updateByIdSelectiveSet">
    UPDATE sys_user
    <set>
        <if test="userName != null and userName != ''">
            user_name = #{userName},
        </if>
        <if test="userPassword != null and userPassword != ''">
            user_password = #{userPassword},
        </if>
        <if test="userEmail != null and userEmail != ''">
            user_email = #{userEmail},
        </if>
        <if test="userInfo != null and userInfo != ''">
            user_info = #{userInfo},
        </if>
        <if test="headImg != null">
            head_img = #{headImg,jdbcType=BLOB},
        </if>
        <if test="createTime != null">
            create_time = #{createTime,jdbcType=TIMESTAMP},
        </if>
        id = #{id},
    </set>
    WHERE id = #{id}
</update>

注意事項:為了避免所有的條件都不滿足,生成的Sql語句沒有set標籤,因此在最後加上了id = #{id},這樣必然存在的賦值。

最後,在SysUserMapperTest測試類中新增如下測試方法:

@Test
public void testUpdateByIdSelectiveSet() {
    SqlSession sqlSession = getSqlSession();

    try {
        SysUserMapper sysUserMapper = sqlSession.getMapper(SysUserMapper.class);

        SysUser sysUser = new SysUser();
        // 更新id=1的使用者
        sysUser.setId(1L);
        // 修改郵箱
        sysUser.setUserEmail("test@mybatis.tk");

        int result = sysUserMapper.updateByIdSelectiveSet(sysUser);
        Assert.assertEquals(1, result);

        // 查詢id=1的使用者
        sysUser = sysUserMapper.selectById(1L);
        // 修改後的名字保持不變,但是郵箱變成了新的
        Assert.assertEquals("admin", sysUser.getUserName());
        Assert.assertEquals("test@mybatis.tk", sysUser.getUserEmail());
    } finally {
        sqlSession.close();
    }
}

執行測試程式碼,測試通過,輸出日誌如下:

DEBUG [main] - ==> Preparing: UPDATE sys_user SET user_email = ?, id = ? WHERE id = ?

DEBUG [main] - ==> Parameters: test@mybatis.tk(String), 1(Long), 1(Long)

DEBUG [main] - <== Updates: 1

DEBUG [main] - ==> Preparing: SELECT id, user_name, user_password, user_email, create_time FROM sys_user WHERE id = ?

DEBUG [main] - ==> Parameters: 1(Long)

TRACE [main] - <== Columns: id, user_name, user_password, user_email, create_time

TRACE [main] - <== Row: 1, admin, 123456, test@mybatis.tk, 2019-06-27 18:21:07.0

DEBUG [main] - <== Total: 1

4. 原始碼

原始碼地址:https://github.com/zwwhnly/mybatis-action.git,歡迎下載。

5. 參考

劉增輝《MyBatis從入門到精通》

相關文章