欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

MyBatis从入门到精通(八):MyBatis动态Sql之foreach标签的用法

程序员文章站 2023-11-03 17:57:52
最近在读刘增辉老师所著的《MyBatis从入门到精通》一书,很有收获,于是将自己学习的过程以博客形式输出,如有错误,欢迎指正,如帮助到你,不胜荣幸! 本篇博客主要讲解如何使用foreach标签生成动态的Sql,主要包含以下3个场景: 1. foreach 实现in集合 2. foreach 实现批量 ......

最近在读刘增辉老师所著的《mybatis从入门到精通》一书,很有收获,于是将自己学习的过程以博客形式输出,如有错误,欢迎指正,如帮助到你,不胜荣幸!

本篇博客主要讲解如何使用foreach标签生成动态的sql,主要包含以下3个场景:

  1. foreach 实现in集合
  2. foreach 实现批量插入
  3. foreach 实现动态update

1. foreach 实现in集合

假设有这样1个需求:根据传入的用户id集合查询出所有符合条件的用户,此时我们需要使用到sql中的in,如 id in (1,1001)。

首先,我们在接口sysusermapper中添加如下方法:

/**
 * 根据用户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 ( ? , ? )

debug [main] - ==> parameters: 1(long), 1001(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

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. 源码

源码地址:,欢迎下载。

5. 参考

刘增辉《mybatis从入门到精通》