MyBatis从入门到精通(八):MyBatis动态Sql之foreach标签的用法
最近在读刘增辉老师所著的《mybatis从入门到精通》一书,很有收获,于是将自己学习的过程以博客形式输出,如有错误,欢迎指正,如帮助到你,不胜荣幸!
本篇博客主要讲解如何使用foreach标签生成动态的sql,主要包含以下3个场景:
- foreach 实现in集合
- foreach 实现批量插入
- 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从入门到精通》
推荐阅读
-
MyBatis从入门到精通(八):MyBatis动态Sql之foreach标签的用法
-
MyBatis从入门到精通(四):MyBatis XML方式的基本用法之增删改
-
MyBatis从入门到精通(七):MyBatis动态Sql之choose,where,set标签的用法
-
MyBatis从入门到精通(二):MyBatis XML方式的基本用法之Select
-
MyBatis从入门到精通(八):MyBatis动态Sql之foreach标签的用法
-
MyBatis从入门到精通(四):MyBatis XML方式的基本用法之增删改
-
MyBatis从入门到精通(七):MyBatis动态Sql之choose,where,set标签的用法
-
MyBatis从入门到精通(二):MyBatis XML方式的基本用法之Select