MyBatis從入門到精通(八):MyBatis動態Sql之foreach標籤的用法

最近在讀劉增輝老師所著的《MyBatis從入門到精通》一書,頗有收穫,因而將本身學習的過程以博客形式輸出,若有錯誤,歡迎指正,如幫助到你,不勝榮幸!java

本篇博客主要講解如何使用foreach標籤生成動態的Sql,主要包含如下3個場景:git

  1. foreach 實現in集合
  2. foreach 實現批量插入
  3. foreach 實現動態update

1. foreach 實現in集合

假設有這樣1個需求:根據傳入的用戶id集合查詢出全部符合條件的用戶,此時咱們須要使用到Sql中的IN,如 id in (1,1001)。github

首先,咱們在接口SysUserMapper中添加以下方法:sql

/** * 根據用戶id集合查詢用戶 * * @param idList * @return */
List<SysUser> selectByIdList(List<Long> idList);
複製代碼

而後在對應的SysUserMapper.xml中添加以下代碼:數據庫

<select id="selectByIdList" resultType="com.zwwhnly.mybatisaction.model.SysUser">
    SELECT id,
    user_name,
    user_password,
    user_email,
    create_time
    FROM sys_user
    WHERE id IN
    <foreach collection="list" open="(" close=")" separator="," item="id" index="i">
        #{id}
    </foreach>
</select>
複製代碼

最後,在SysUserMapperTest測試類中添加以下測試方法:數組

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

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

        List<Long> idList = new ArrayList<Long>();
        idList.add(1L);
        idList.add(1001L);

        List<SysUser> userList = sysUserMapper.selectByIdList(idList);
        Assert.assertEquals(2, userList.size());
    } finally {
        sqlSession.close();
    }
}
複製代碼

運行測試代碼,測試經過,輸出日誌以下:微信

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

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

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

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

DEBUG [main] - <== Total: 2

經過日誌會發現,foreach元素中的內容最終生成的Sql語句爲(1,1001)。

foreach包含屬性講解:

  • open:整個循環內容開頭的字符串。
  • close:整個循環內容結尾的字符串。
  • separator:每次循環的分隔符。
  • item:從迭代對象中取出的每個值。
  • index:若是參數爲集合或者數組,該值爲當前索引值,若是參數爲Map類型時,該值爲Map的key。
  • collection:要迭代循環的屬性名。

也許有人會好奇,爲何collection的值是list?該值該如何設置呢?

上面的例子中只有一個集合參數,咱們把collection屬性的值設置爲了list,其實也能夠寫成collection,爲何呢?讓咱們看下DefaultSqlSession中的默認處理邏輯:

private Object wrapCollection(Object object) {
    DefaultSqlSession.StrictMap map;
    if (object instanceof Collection) {
        map = new DefaultSqlSession.StrictMap();
        map.put("collection", object);
        if (object instanceof List) {
            map.put("list", object);
        }

        return map;
    } else if (object != null && object.getClass().isArray()) {
        map = new DefaultSqlSession.StrictMap();
        map.put("array", object);
        return map;
    } else {
        return object;
    }
}
複製代碼

雖然使用默認值,代碼也能夠正常運行,但仍是推薦使用@Param來指定參數的名字,以下所示:

List<SysUser> selectByIdList(@Param("idList") List<Long> idList);
複製代碼
<foreach collection="idList" open="(" close=")" separator="," item="id" index="i">
    #{id}
</foreach>
複製代碼

若是參數是一個數組參數,collection能夠設置爲默認值array,看了上面的源碼,應該不難理解。

/** * 根據用戶id數組查詢用戶 * * @param idArray * @return */
List<SysUser> selectByIdArray(Long[] idArray);
複製代碼
<select id="selectByIdArray" resultType="com.zwwhnly.mybatisaction.model.SysUser">
    SELECT id,
    user_name,
    user_password,
    user_email,
    create_time
    FROM sys_user
    WHERE id IN
    <foreach collection="array" open="(" close=")" separator="," item="id" index="i">
        #{id}
    </foreach>
</select>
複製代碼

雖然使用默認值,代碼也能夠正常運行,但仍是推薦使用@Param來指定參數的名字,以下所示:

List<SysUser> selectByIdArray(@Param("idArray")Long[] idArray);
複製代碼
<foreach collection="idArray" open="(" close=")" separator="," item="id" index="i">
    #{id}
</foreach>
複製代碼

2. foreach 實現批量插入

假設有這樣1個需求:將傳入的用戶集合批量寫入數據庫。

首先,咱們在接口SysUserMapper中添加以下方法:

/** * 批量插入用戶信息 * * @param userList * @return */
int insertList(List<SysUser> userList);
複製代碼

而後在對應的SysUserMapper.xml中添加以下代碼:

<insert id="insertList" useGeneratedKeys="true" keyProperty="id">
    INSERT INTO sys_user(user_name, user_password, user_email, user_info, head_img, create_time)
    VALUES
    <foreach collection="list" item="user" separator=",">
        (#{user.userName},#{user.userPassword},#{user.userEmail},#{user.userInfo},#{user.headImg,jdbcType=BLOB},#{user.createTime,jdbcType=TIMESTAMP})
    </foreach>
</insert>
複製代碼

最後,在SysUserMapperTest測試類中添加以下測試方法:

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

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

        List<SysUser> sysUserList = new ArrayList<SysUser>();
        for (int i = 0; i < 2; i++) {
            SysUser sysUser = new SysUser();
            sysUser.setUserName("test" + i);
            sysUser.setUserPassword("123456");
            sysUser.setUserEmail("test@mybatis.tk");

            sysUserList.add(sysUser);
        }

        int result = sysUserMapper.insertList(sysUserList);

        for (SysUser sysUser : sysUserList) {
            System.out.println(sysUser.getId());
        }

        Assert.assertEquals(2, result);
    } finally {
        sqlSession.close();
    }
}
複製代碼

運行測試代碼,測試經過,輸出日誌以下:

DEBUG [main] - ==> Preparing: INSERT INTO sys_user(user_name, user_password, user_email, user_info, head_img, create_time) VALUES (?,?,?,?,?,?) , (?,?,?,?,?,?)

DEBUG [main] - ==> Parameters: test0(String), 123456(String), test@mybatis.tk(String), null, null, null, test1(String), 123456(String), test@mybatis.tk(String), null, null, null

DEBUG [main] - <== Updates: 2

1035

1036

3. foreach 實現動態update

假設有這樣1個需求:根據傳入的Map參數更新用戶信息。

首先,咱們在接口SysUserMapper中添加以下方法:

/** * 經過Map更新列 * * @param map * @return */
int updateByMap(Map<String, Object> map);
複製代碼

而後在對應的SysUserMapper.xml中添加以下代碼:

<update id="updateByMap">
    UPDATE sys_user
    SET
    <foreach collection="_parameter" item="val" index="key" separator=",">
        ${key} = #{val}
    </foreach>
    WHERE id = #{id}
</update>
複製代碼

最後,在SysUserMapperTest測試類中添加以下測試方法:

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

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

        Map<String, Object> map = new HashMap<String, Object>();
        map.put("id", 1L);
        map.put("user_email", "test@mybatis.tk");
        map.put("user_password", "12345678");

        Assert.assertEquals(1, sysUserMapper.updateByMap(map));

        SysUser sysUser = sysUserMapper.selectById(1L);
        Assert.assertEquals("test@mybatis.tk", sysUser.getUserEmail());
        Assert.assertEquals("12345678", sysUser.getUserPassword());
    } finally {
        sqlSession.close();
    }
}
複製代碼

運行測試代碼,測試經過,輸出日誌以下:

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

DEBUG [main] - ==> Parameters: test@mybatis.tk(String), 12345678(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, 12345678, test@mybatis.tk, 2019-06-27 18:21:07.0

DEBUG [main] - <== Total: 1

上面示例中,collection使用的是默認值_parameter,也可使用@Param指定參數名字,以下所示:

int updateByMap(@Param("userMap") Map<String, Object> map);
複製代碼
<update id="updateByMap">
    UPDATE sys_user
    SET
    <foreach collection="userMap" item="val" index="key" separator=",">
        ${key} = #{val}
    </foreach>
    WHERE id = #{userMap.id}
</update>
複製代碼

4. 源碼及參考

源碼地址:github.com/zwwhnly/myb…,歡迎下載。

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

5. 最後

打個小廣告,歡迎掃碼關注微信公衆號:「申城異鄉人」,按期分享Java技術乾貨,讓咱們一塊兒進步。

相關文章
相關標籤/搜索