• 概述
  • foreach实现in集合
    • 1.需求
    • 2.UserMapper接口增加接口方法
    • 3.UserMapper.xml增加动态SQL
    • 4.单元测试
  • foreach实现批量插入
  • 前提
    • 1.需求
    • 2.UserMapper接口增加接口方法
    • 3.UserMapper.xml增加动态SQL
    • 4.单元测试
  • foreach实现动态update
    • 不使用@Param注解指定参数名的情况

      • 1.UserMapper接口
      • 2.UserMapper.xml动态SQL
    • 使用@Param注解指定参数名的情况
      • 1.UserMapper接口
      • 2.UserMapper.xml动态SQL
      • 3,单元测试

概述

SQL语句中有时候会使用IN关键字,比如 id in (1,2,3,4)。

虽然可以使用${ids}方式直接获取值,但${ids}不能防止SQL注入, 想要避免SQL注入就需要用#{}的方式,这时就要配合使用foreach标签来满足需求.

foreach可以对数组、Map或者实现了Iterable接口(比如List、Set)的对象进行遍历。 数组在处理的时候可以转换为List对象。 因此foreach遍历的对象可以分为两大类

  • Iterable类型
  • Map类型。

这两种类型在遍历循环时情况是不一样的,我们通过如下3个示例来讲解foreach的用法


foreach实现in集合

foreach实现in集合(或者数组)是最简单和常见的一种情况


1.需求

根据id集合查出所有符合条件的用户


2.UserMapper接口增加接口方法

/*** * * @Title: selectSysUserByIdList* * @Description: 根据用户ID集合查询用户* * @param ids* @return* * @return: List<SysUser>*/List<SysUser> selectSysUserByIdList(List<Long> ids);

3.UserMapper.xml增加动态SQL

    <select id="selectSysUserByIdList" resultType="com.artisan.mybatis.xml.domain.SysUser">SELECTa.id,a.user_name userName,a.user_password userPassword,a.user_email userEmail,a.user_info userInfo,a.head_img headImg,a.create_time createTimeFROMsys_user aWHERE id in <foreach collection="list" item="userId" open="(" close=")" separator="," index="i">#{userId}</foreach></select>

foreach的属性

  • collection 必填,值为要迭代循环的属性名。 情况有很多种
  • item 变量名,值为从迭代对象中取出的每一个值
  • index 索引的属性名,在集合数组请鲁昂下为当前索引值,的那个迭代循环的对象是Map类型时,这个值为Map的key(键值)
  • open 整个循环内容开头的字符串
  • close 整个循环内容结尾的字符串
  • separator 每次循环的分隔符

4.单元测试

    @Testpublic void selectSysUserByIdListTest() {logger.info("selectSysUserByIdListTest");// 获取SqlSessionSqlSession sqlSession = getSqlSession();try {// 获取UserMapper接口UserMapper userMapper = sqlSession.getMapper(UserMapper.class);// 模拟idListList<Long> idList = new ArrayList<Long>();idList.add(1L);idList.add(1001L);// 调用接口方法List<SysUser> userList = userMapper.selectSysUserByIdList(idList);// userList不为空Assert.assertNotNull(userList);// userList > 0Assert.assertTrue(userList.size() > 0);// 期望返回2条数据,符合数据库中记录Assert.assertEquals(2, userList.size());logger.info(userList);} catch (Exception e) {e.printStackTrace();} finally {sqlSession.close();logger.info("sqlSession close successfully ");}}

日志

2018-04-23 01:49:29,686  INFO [main] (BaseMapperTest.java:26) - sessionFactory bulit successfully
2018-04-23 01:49:29,692  INFO [main] (BaseMapperTest.java:29) - reader close successfully
2018-04-23 01:49:29,696  INFO [main] (UserMapperTest.java:729) - selectSysUserByIdListTest
2018-04-23 01:49:30,203 DEBUG [main] (BaseJdbcLogger.java:145) - ==>  Preparing: SELECT a.id, a.user_name userName, a.user_password userPassword, a.user_email userEmail, a.user_info userInfo, a.head_img headImg, a.create_time createTime FROM sys_user a WHERE id in ( ? , ? )
2018-04-23 01:49:30,267 DEBUG [main] (BaseJdbcLogger.java:145) - ==> Parameters: 1(Long), 1001(Long)
2018-04-23 01:49:30,295 TRACE [main] (BaseJdbcLogger.java:151) - <==    Columns: id, userName, userPassword, userEmail, userInfo, headImg, createTime
2018-04-23 01:49:30,296 TRACE [main] (BaseJdbcLogger.java:151) - <==        Row: 1, admin, 123456, admin@artisan.com, <<BLOB>>, <<BLOB>>, 2018-04-13 21:12:47.0
2018-04-23 01:49:30,304 TRACE [main] (BaseJdbcLogger.java:151) - <==        Row: 1001, artisan, 123456, test@artisan.com, <<BLOB>>, <<BLOB>>, 2018-04-13 21:12:47.0
2018-04-23 01:49:30,305 DEBUG [main] (BaseJdbcLogger.java:145) - <==      Total: 2
2018-04-23 01:49:30,306  INFO [main] (UserMapperTest.java:747) - [SysUser [id=1, userName=admin, userPassword=123456, userEmail=admin@artisan.com, userInfo=管理员用户, headImg=[18, 49, 35, 18, 48], createTime=Fri Apr 13 21:12:47 BOT 2018], SysUser [id=1001, userName=artisan, userPassword=123456, userEmail=test@artisan.com, userInfo=测试用户, headImg=[18, 49, 35, 18, 48], createTime=Fri Apr 13 21:12:47 BOT 2018]]
2018-04-23 01:49:30,314  INFO [main] (UserMapperTest.java:752) - sqlSession close successfully

foreach实现批量插入

前提

如果数据库支持批量插入,就可以通过foreach实现。 批量插入是SQL-92新增的特性,目前支持的数据库有DB2、SQL Server 2008+、PostgreSql8.2+、MySQL、SQLite3.7.11+ 以及H2.

语法

insert into tablename(column-a,[column-b,....])values('value-1a',['value-1b',...]),('value-2a',['value-2b',...]),('value-3a',['value-3b',...]),......

从上述语法部分可以看到,后面是一个值的循环,因此可以通过foreach来实现循环插入。


1.需求

批量插入用户


2.UserMapper接口增加接口方法

/*** * * @Title: insertSysUserList* * @Description: 批量新增用户* * @param sysUserList* @return* * @return: int*/int insertSysUserList(List<SysUser> sysUserList);

3.UserMapper.xml增加动态SQL

<insert id="insertSysUserList"  keyProperty="id" useGeneratedKeys="true">insert into sys_user(user_name, user_password, user_email, user_info, head_img, create_time)values<foreach collection="list" item="sysUser" separator=",">(#{sysUser.userName}, #{sysUser.userPassword}, #{sysUser.userEmail}, #{sysUser.userInfo}, #{sysUser.headImg, jdbcType=BLOB},#{sysUser.createTime, jdbcType=TIMESTAMP})</foreach></insert>

通过item指定了循环变量名后,在引用值的时候使用的是“属性.属性”的方式,如上所示sysUser.userName


4.单元测试

@Testpublic void insertSysUserListTest() {logger.info("insertSysUserListTest");// 获取SqlSessionSqlSession sqlSession = getSqlSession();try {// 获取UserMapper接口UserMapper userMapper = sqlSession.getMapper(UserMapper.class);// 模拟userListList<SysUser> userList = new ArrayList<SysUser>();for (int i = 0; i < 5; i++) {SysUser sysUser = new SysUser();sysUser.setUserName("artisanTest_" + i);sysUser.setUserPassword("123456_" + i);sysUser.setUserEmail("artisan_" + i + "@artisan.com");sysUser.setUserInfo("测试用户" + i);// 模拟头像sysUser.setHeadImg(new byte[] { 1, 2, 3 });sysUser.setCreateTime(new Date());// 添加到SysUseruserList.add(sysUser);}// 新增用户 ,返回受影响的行数int result = userMapper.insertSysUserList(userList);// 返回批量的自增主键 配合 keyProperty="id" useGeneratedKeys="true" 这两个属性for (SysUser sysUser : userList) {logger.info(sysUser.getId());}// 只插入一条数据 ,期望是5Assert.assertEquals(5, result);// 重新查询List<SysUser> sysUserList = userMapper.selectAll();// 根据数据库之前的2条记录,加上本次新增的5条(虽未提交但还是在一个会话中,所以可以查询的到)Assert.assertNotNull(sysUserList);Assert.assertEquals(7, sysUserList.size());} catch (Exception e) {e.printStackTrace();} finally {// 为了保持测试数据的干净,这里选择回滚// 由于默认的sqlSessionFactory.openSession()是不自动提交的// 除非显式的commit,否则不会提交到数据库sqlSession.rollback();logger.info("为了保持测试数据的干净,这里选择回滚,不写入mysql,请观察日志,回滚完成");sqlSession.close();logger.info("sqlSession close successfully ");}}

日志

2018-04-23 15:31:28,500  INFO [main] (BaseMapperTest.java:26) - sessionFactory bulit successfully
2018-04-23 15:31:28,505  INFO [main] (BaseMapperTest.java:29) - reader close successfully
2018-04-23 15:31:28,508  INFO [main] (UserMapperTest.java:761) - insertSysUserListTest
2018-04-23 15:31:29,091 DEBUG [main] (BaseJdbcLogger.java:145) - ==>  Preparing: insert into sys_user( user_name, user_password, user_email, user_info, head_img, create_time) values ( ?, ?, ?, ?, ?, ? ) , ( ?, ?, ?, ?, ?, ? ) , ( ?, ?, ?, ?, ?, ? ) , ( ?, ?, ?, ?, ?, ? ) , ( ?, ?, ?, ?, ?, ? )
2018-04-23 15:31:29,183 DEBUG [main] (BaseJdbcLogger.java:145) - ==> Parameters: artisanTest_0(String), 123456_0(String), artisan_0@artisan.com(String), 测试用户0(String), java.io.ByteArrayInputStream@2f0496f0(ByteArrayInputStream), 2018-04-23 15:31:28.526(Timestamp), artisanTest_1(String), 123456_1(String), artisan_1@artisan.com(String), 测试用户1(String), java.io.ByteArrayInputStream@56517ead(ByteArrayInputStream), 2018-04-23 15:31:28.526(Timestamp), artisanTest_2(String), 123456_2(String), artisan_2@artisan.com(String), 测试用户2(String), java.io.ByteArrayInputStream@53bc21(ByteArrayInputStream), 2018-04-23 15:31:28.526(Timestamp), artisanTest_3(String), 123456_3(String), artisan_3@artisan.com(String), 测试用户3(String), java.io.ByteArrayInputStream@79641ab1(ByteArrayInputStream), 2018-04-23 15:31:28.526(Timestamp), artisanTest_4(String), 123456_4(String), artisan_4@artisan.com(String), 测试用户4(String), java.io.ByteArrayInputStream@1b1498ba(ByteArrayInputStream), 2018-04-23 15:31:28.526(Timestamp)
2018-04-23 15:31:29,190 DEBUG [main] (BaseJdbcLogger.java:145) - <==    Updates: 5
2018-04-23 15:31:29,191  INFO [main] (UserMapperTest.java:789) - 1032
2018-04-23 15:31:29,191  INFO [main] (UserMapperTest.java:789) - 1033
2018-04-23 15:31:29,191  INFO [main] (UserMapperTest.java:789) - 1034
2018-04-23 15:31:29,192  INFO [main] (UserMapperTest.java:789) - 1035
2018-04-23 15:31:29,192  INFO [main] (UserMapperTest.java:789) - 1036
2018-04-23 15:31:29,196 DEBUG [main] (BaseJdbcLogger.java:145) - ==>  Preparing: select a.id , a.user_name userName, a.user_password userPassword, a.user_email userEmail, a.user_info userInfo, a.head_img headImg, a.create_time createTime from sys_user a
2018-04-23 15:31:29,197 DEBUG [main] (BaseJdbcLogger.java:145) - ==> Parameters:
2018-04-23 15:31:29,231 TRACE [main] (BaseJdbcLogger.java:151) - <==    Columns: id, userName, userPassword, userEmail, userInfo, headImg, createTime
2018-04-23 15:31:29,231 TRACE [main] (BaseJdbcLogger.java:151) - <==        Row: 1, admin, 123456, admin@artisan.com, <<BLOB>>, <<BLOB>>, 2018-04-13 21:12:47.0
2018-04-23 15:31:29,243 TRACE [main] (BaseJdbcLogger.java:151) - <==        Row: 1001, artisan, 123456, test@artisan.com, <<BLOB>>, <<BLOB>>, 2018-04-13 21:12:47.0
2018-04-23 15:31:29,247 TRACE [main] (BaseJdbcLogger.java:151) - <==        Row: 1032, artisanTest_0, 123456_0, artisan_0@artisan.com, <<BLOB>>, <<BLOB>>, 2018-04-23 15:31:29.0
2018-04-23 15:31:29,248 TRACE [main] (BaseJdbcLogger.java:151) - <==        Row: 1033, artisanTest_1, 123456_1, artisan_1@artisan.com, <<BLOB>>, <<BLOB>>, 2018-04-23 15:31:29.0
2018-04-23 15:31:29,249 TRACE [main] (BaseJdbcLogger.java:151) - <==        Row: 1034, artisanTest_2, 123456_2, artisan_2@artisan.com, <<BLOB>>, <<BLOB>>, 2018-04-23 15:31:29.0
2018-04-23 15:31:29,250 TRACE [main] (BaseJdbcLogger.java:151) - <==        Row: 1035, artisanTest_3, 123456_3, artisan_3@artisan.com, <<BLOB>>, <<BLOB>>, 2018-04-23 15:31:29.0
2018-04-23 15:31:29,251 TRACE [main] (BaseJdbcLogger.java:151) - <==        Row: 1036, artisanTest_4, 123456_4, artisan_4@artisan.com, <<BLOB>>, <<BLOB>>, 2018-04-23 15:31:29.0
2018-04-23 15:31:29,251 DEBUG [main] (BaseJdbcLogger.java:145) - <==      Total: 7
2018-04-23 15:31:29,255  INFO [main] (UserMapperTest.java:807) - 为了保持测试数据的干净,这里选择回滚,不写入mysql,请观察日志,回滚完成
2018-04-23 15:31:29,256  INFO [main] (UserMapperTest.java:810) - sqlSession close successfully

foreach实现动态update

这部分我们主要介绍当参数类型是Map的时候,foreach如何实现动态UPDATE

当参数是Map类型的时候,foreach标签的index属性值对应的不是索引值,而是Map中的key, 利用这个key就可以动态实现UPDATE了。


不使用@Param注解指定参数名的情况

1.UserMapper接口

void updateSysUserByMap(Map<String, Object> map);

这里没有使用@Parma注解指定参数名,因而MyBatis在内部的上线文中使用默认值 _parameter 最为该参数的key ,所以xml中也必须使用_parameter。


2.UserMapper.xml动态SQL

<update id="updateSysUserByMap">update sys_user set <foreach collection="_parameter"  item="value"  index="key"  separator=",">${key} = #{value}</foreach>where id = #{id}</update>

这里的key作为列名,对应的值作为该列的值,通过foreach将需要更新的字段拼接在SQL语句中。


使用@Param注解指定参数名的情况

1.UserMapper接口

void updateSysUserByMapWithParam(@Param("userMap") Map<String, Object> map);

2.UserMapper.xml动态SQL

<update id="updateSysUserByMapWithParam">update sys_user set <foreach collection="userMap"  item="value"  index="key"  separator=",">${key} = #{value}</foreach>where id = #{userMap.id}</update>

3,单元测试

@Testpublic void updateSysUserByMapTest() {logger.info("updateSysUserByMapTest");// 获取SqlSessionSqlSession sqlSession = getSqlSession();try {// 获取UserMapper接口UserMapper userMapper = sqlSession.getMapper(UserMapper.class);// 模拟MapMap<String, Object> userMap = new HashMap<String, Object>();// 查询条件,同时也是where后面的更新字段, 必须存在userMap.put("id", 1L);// 更新其他字段userMap.put("user_email", "map@artisan.com");userMap.put("user_name", "ARTISAN_ADMIN");// 调用接口,更新数据// userMapper.updateSysUserByMap(userMap);// 或者userMapper.updateSysUserByMapWithParam(userMap);// 根据当前id 查询用户SysUser sysUser = userMapper.selectSysUserById(1L);Assert.assertEquals("map@artisan.com", sysUser.getUserEmail());Assert.assertEquals("ARTISAN_ADMIN", sysUser.getUserName());} catch (Exception e) {e.printStackTrace();} finally {// 为了保持测试数据的干净,这里选择回滚// 由于默认的sqlSessionFactory.openSession()是不自动提交的// 除非显式的commit,否则不会提交到数据库sqlSession.rollback();logger.info("为了保持测试数据的干净,这里选择回滚,不写入mysql,请观察日志,回滚完成");sqlSession.close();logger.info("sqlSession close successfully ");}}
2018-04-23 16:27:06,658  INFO [main] (BaseMapperTest.java:26) - sessionFactory bulit successfully
2018-04-23 16:27:06,661  INFO [main] (BaseMapperTest.java:29) - reader close successfully
2018-04-23 16:27:06,664  INFO [main] (UserMapperTest.java:820) - updateSysUserByMapTest
2018-04-23 16:27:07,243 DEBUG [main] (BaseJdbcLogger.java:145) - ==>  Preparing: update sys_user set id = ? , user_name = ? , user_email = ? where id = ?
2018-04-23 16:27:07,319 DEBUG [main] (BaseJdbcLogger.java:145) - ==> Parameters: 1(Long), ARTISAN_ADMIN(String), map@artisan.com(String), 1(Long)
2018-04-23 16:27:07,325 DEBUG [main] (BaseJdbcLogger.java:145) - <==    Updates: 1
2018-04-23 16:27:07,327 DEBUG [main] (BaseJdbcLogger.java:145) - ==>  Preparing: select a.id, a.user_name, a.user_password, a.user_email, a.user_info, a.head_img, a.create_time from sys_user a where id = ?
2018-04-23 16:27:07,328 DEBUG [main] (BaseJdbcLogger.java:145) - ==> Parameters: 1(Long)
2018-04-23 16:27:07,364 TRACE [main] (BaseJdbcLogger.java:151) - <==    Columns: id, user_name, user_password, user_email, user_info, head_img, create_time
2018-04-23 16:27:07,365 TRACE [main] (BaseJdbcLogger.java:151) - <==        Row: 1, ARTISAN_ADMIN, 123456, map@artisan.com, <<BLOB>>, <<BLOB>>, 2018-04-13 21:12:47.0
2018-04-23 16:27:07,370 DEBUG [main] (BaseJdbcLogger.java:145) - <==      Total: 1
2018-04-23 16:27:07,373  INFO [main] (UserMapperTest.java:852) - 为了保持测试数据的干净,这里选择回滚,不写入mysql,请观察日志,回滚完成
2018-04-23 16:27:07,374  INFO [main] (UserMapperTest.java:855) - sqlSession close successfully

MyBatis-14MyBatis动态SQL之【foreach】相关推荐

  1. MyBatis中动态sql的模糊搜索、foreach实现In集合的用法

    场景 在使用MyBatis的动态sql时,常见的是传递一个ID的数组,查询记录的 ID在这个数组中的记录和模糊搜索这两种场景. 注: 博客: https://blog.csdn.net/badao_l ...

  2. MyBatis的动态SQL详解

    MyBatis的动态SQL是基于OGNL表达式的,它可以帮助我们方便的在SQL语句中实现某些逻辑. MyBatis中用于实现动态SQL的元素主要有:   if choose(when,otherwis ...

  3. MyBatis中动态sql实现时间范围比较的查询

    场景 前端传递两个时间参数,开始时间和结束时间,然后从数据库中筛选出某个时间属性在此范围的数据. Mybatis的动态sql的写法. 注: 博客: https://blog.csdn.net/bada ...

  4. mybatis的动态sql的一些记录

    动态sql的作用:传统的使用JDBC的方法,相信大家在组合复杂的的SQL语句的时候,需要去拼接,稍不注意哪怕少了个空格,都会导致错误.Mybatis的动态SQL功能正是为了解决这种问题, 其通过 if ...

  5. MyBatis(三)——动态SQL

    文章目录 1. 简介 2. 搭建环境 2.1 在MySQL中创建blog表 2.2 编写实体类 2.3 编写实体类对应Mapper接口 2.4 编写Mapper接口对应的Mapper.xml文件 2. ...

  6. 9、mybatis中动态sql的使用

    对于初学者,如何进行mybatis的学习呢?我总结了几点,会慢慢的更新出来.首先大家需要了解mybatis是什么.用mybatis来做什么.为什么要用mybatis.有什么优缺点:当知道了为什么的时候 ...

  7. 利用MyBatis的动态SQL特性抽象统一SQL查询接口

    1. SQL查询的统一抽象 MyBatis制动动态SQL的构造,利用动态SQL和自定义的参数Bean抽象,可以将绝大部分SQL查询抽象为一个统一接口,查询参数使用一个自定义bean继承Map,使用映射 ...

  8. 2022/5/1 Mybatis框架动态SQL

    目录 1丶动态 SQL 2丶if标签 3丶choose.when.otherwise 4丶trim.where.set 5丶foreach 6丶script 7丶bind 8丶多数据库支持 9丶动态 ...

  9. MyBatis 03 动态SQL

    MyBatis 03 动态SQL 文章目录 MyBatis 03 动态SQL 一.学习目标 二.动态SQL if 标签2-1 if 标签2-2 where标签2-1 where 标签2-2 choos ...

  10. Mybatis学习-动态SQL

    Mybatis学习-动态SQL Mybatis学习-动态SQL Mybatis学习-动态SQL 什么是动态SQL 搭建环境 IF 接口 Mapper 测试 trim (where, set) wher ...

最新文章

  1. 导出数据库API接口文档
  2. Magento如何自定义404页面?
  3. 我自己为我自己定制的文章模板
  4. iframe和父窗体之间的互相监听方法
  5. 安装electron-react-boilerplate遇到的问题
  6. git 创建webpack项目_使用webpack手动创建一个完整项目的全过程
  7. Linux正在更新缓存卡住,Linux 进程卡住了怎么办?
  8. aws rds监控慢sql_AWS RDS SQL Server中的高级Windows身份验证配置
  9. 关于android输入框被键盘遮挡的问题
  10. shield tv android tv,NVIDIA老机顶盒SHIELD TV升级安卓7.0:国行眼巴巴
  11. fastmock模拟常见数据结构
  12. 如何实现用手机远程控制电脑?
  13. 比较器应用一:滞回比较器
  14. window.open() 被拦截的问题解决
  15. 《人生要耐得住寂寞》
  16. 微信朋友圈卖葡萄经验分享
  17. 安卓使用ContentProvider实现读取手机联系人和短信内容
  18. Docker各操作系统安装方式及优缺点
  19. android联系人管理源码,计算机毕业课程设计源码-145安卓Android通讯录管理系统
  20. 企业如何建立数据分类分级制度

热门文章

  1. oracle sql练习_数据分析之学习SQL
  2. 148. Leetcode 455. 分发饼干 (贪心算法-基础题目)
  3. Leetcode 67. 二进制求和 (每日一题 20210826)
  4. 判断两个字符串是否是变形词
  5. 认识哈希函数(散列函数)
  6. python爬虫实战(一)~爬取百度百科人物的文本+图片信息+Restful api接口
  7. 概率统计概念复习:MAPMLE
  8. 10分钟带你入门MATLAB
  9. 用Tableau制作滚动时间轴(下)
  10. 利用shell脚本来监控linux系统的内存