网站建设还能赚钱吗,做英文网站有哪些,企业免费邮箱注册申请,企业网站官网文章目录 一. 较复杂的查询操作1. 参数占位符#{}和${}2. SQL注入3. like查询4. resultType与resultMap5. 多表查询5.1. 一对一表映射5.2. 一对多表映射 二. 动态SQL1. if标签2. trim标签3. where标签4. set标签5. foreach标签 本篇中使用的数据表即基础映射类都是基于上一篇博客… 文章目录 一. 较复杂的查询操作1. 参数占位符#{}和${}2. SQL注入3. like查询4. resultType与resultMap5. 多表查询5.1. 一对一表映射5.2. 一对多表映射 二. 动态SQL1. if标签2. trim标签3. where标签4. set标签5. foreach标签 本篇中使用的数据表即基础映射类都是基于上一篇博客的如果有需要可以移步
MyBatis配置及单表操作 一. 较复杂的查询操作
1. 参数占位符#{}和${}
#{}预处理符生成的执行 sql 中将id#{6}替换为id?最后使用6替换了?作为参数。 ${}替换符生成的执行 sql 中将id${6}替换为id6直接替换属于字符串拼接。
两种占位符都可以正常使用的场合传入的参数类型是数值类型。
使用${}
select * from userinfo where id${id}
select * from userinfo where id6使用#{}
select * from userinfo where id#{id}
select * from userinfo where id?对于这两种参数占位符都能使用的情况下更建议使用#{}就使用#{}因为${}存在 SQL 注入的问题以及在特殊场景下如果传入的类型是字符串也会出问题。
只能使用#{}而不能使用${}的场合传入参数类型为 String
使用${}
select * from userinfo where username${username}
//实际执行的语句 (比如前端通过 URL 传入的参数)
Preparing: select * from userinfo where username张三但是在 sql 中通过字符串来查询数据是需要加上引号的而使用${}生成的sql并没有带引号因此不适用于字符串参数的sql。
使用#{}
select * from userinfo where username#{username}
//实际执行语句
select * from userinfo where username?由于使用#{}是将目标参数替换为占位符然后利用 JDBC 中的占位符机制实现 sql 语句的填充最终是会带上引号的所以使用#{}构造的 sql 是可以正常运行的并且没有 SQL 注入的问题。
所以#{}相比于${}它支持所有类型的参数包括数值类与字符串类而${}支持数值类型不支持字符串类型的参数但也可以在原来 sql 里面为${}外面加上一对引号来使用。
select * from userinfo where username${username}
//实际执行语句
select * from userinfo where username张三当传递的参数为字符串类型的时候虽然加上一对引号使用${}也可以使用但是${}存在 SQL 注入问题所以仍然不推荐有关 SQL 注入后面在下文有介绍。
大部分场合下使用#{}都可以解决但还是存在一小部分只能是${}来处理的。
如当我们需要按照升序或者逆序得到数据库查询结果的时候这种场合就只能使用${}使用#{}会报错我们来演示一下。
首先我们在Mapper接口中声明一个方法作用就是按照排序获取结果集
ListUserinfo getAllByOrder(Param(myOrder) String myOrder);我们再去xml文件中去写 sql 语句首先我们使用$进行演示
select idgetAllByOrder resultTypecom.example.demo.model.UserInfoselect * from userinfo order by id ${myOrder}
/select写一个单元测试代码很简单就是调用 sql然后得到结果集
Test
void getOrderList() {ListUserInfo userMappers userMapper.getOrderList(desc);System.out.println(userMappers);
}单元测试结果 可以正常查询得到的结果与预期也是相同的。 我们再来试一试使用#{}来构造 sql 语句
select idgetOrderList resultTypecom.example.demo.model.UserInfoselect * from userinfo order by createtime #{order};
/select重新执行单元测试看下面的结果 我们发现程序报错了这是因为使用了desc的字符串带引号替换了占位符而我们所需要的不是一个desc字符串而是直接一个desc的关键字不带引号所以 sql 也抛出了语法错误这里最终执行的 sql 如下
select * from userinfo order by createtime ‘desc’;而期望执行的 sql 为
select * from userinfo order by createtime desc;所以在传递 sql 命令关键字的时候不能使用#{}只能使用${}。
总之如果不得不使用${}那么传递的参数一定要能被穷举此时才能在业务代码中对传递的值进行安全校验否则是不能使用的。
2. SQL注入
SQL 注入就是使用${}实现一些特殊的语句来达到非法获取数据的目的如不通过正确的密码获取某账户的信息下面我们来演示一下就以登录的例子来演示SQL 注入可以在不知道密码的前提下登录成功并且获取到用户的相关信息。
首先我将数据库只保留一个用户信息方便演示 SQL 注入问题
1️⃣第一步在Mapper接口中定义方法login返回登录成功的用户对象。
public UserInfo login(Param(username) String username, Param((password)) String password);2️⃣第二步在xml文件中编写 sql 语句我们需要演示 SQL 注入所以我们使用${}来构造 sql 语句。
select idlogin resultTypecom.example.demo.model.UserInfoselect * from userinfo where username${username} and password${password};
/select3️⃣第三步编写单元测试传入有注入问题的 sql 语句 or 11此时不需要密码就能拿到相关的用户信息。
Test
void login() {String username admin;String password or 11;UserInfo userInfo userMapper.login(username, password);System.out.println(userInfo);
}结果如下 我们在不知道用户密码的情况下就登录成功并拿到了用户的信息。 最终执行的 sql 语句为
select * from userinfo where usernameadmin and password or 11;相当于它在原来条件判断的语句下后面有加上一个或的逻辑并且或后面的表达式为true这样就使得原来的 SQL 语句中的条件判断部分一定为真所以就在没有正确密码的情况下拿到了用户的基本信息。
所以我们能不使用${}就不使用${}它存在 SQL 注入问题如果必须使用${}则需要验证一下传递的参数是否合法比如上面定义排序的 sql传递的参数只能是desc或者是asc如果不是就不能执行这条 SQL防止 SQL 注入情况的发生。
3. like查询
在 Mybatis 中使用like查询比较特殊因为直接使用#{}会报错而使用${}由于输入的字符串情况很多无法做到枚举验证比较困难无法避免 SQL 注入问题。
首先我们来演示使用#{}进行like查询。
1️⃣第一步声明方法。
ListUserInfo getLikeList(Param(username) String username);2️⃣第二步xml 中实现 执行 sql。
select idgetLikeList resultTypecom.example.demo.model.UserInfoselect * from userinfo where username like %#{username}%
/select3️⃣第三步单元测试。
Test
void getLikeList() {String username a;ListUserInfo list userMapper.getLikeList(username);System.out.println(list);
}结果如下
报错了因为#{}会被替换成一个字符串而在这个%#{username}%语句中#{username}是不能带上引号的带上就违背 SQL 语法执行就会出错。 使用#{}会多出一对引号而使用${}又无法穷举出所有情况进行验证就可能有 SQL 注入问题所以这里是不能直接使用#{}的我们需要搭配 MySQL 内置的字符串拼接语句concat。
我们将 sql 改为使用concat进行字符串拼接
select idgetLikeList resultTypecom.example.demo.model.UserInfoselect * from userinfo where username like concat(%, #{username}, %)
/select重新执行单元测试此时就可以正常执行了。
4. resultType与resultMap
resultType表示数据库返回的数据映射在 java 程序中所对应的类型只要定义类中的属性与数据库中表的字段名字一致就没有任何问题但是如果不一致冲突的属性就无法获取到数据库查询的结果。
比如用户名属性在数据库中的名字是username而在 java 程序类中的属性名为name此时通过 mybatis 将数据传递到程序中的对象时获取到的name属性为null就不能正确地获取到对应的属性值为了解决这个数据库字段与类中中字段不匹配的问题我们需要使用到resultMap。
resultMap 的使用方式就是在xml文件中设置resultMap标签至少需要设置两个属性一个是id表示你这个 resultMap 标签的名字还有一个是type属性它表示映射到程序中类的类型需包含包名。
这个标签里面需要设置至少两个子标签一个是id标签另外一个是result标签前者表示主键后者表示数据库表中普通的列这两种标签也是至少需要设置两个属性一个是column表示数据库表中的字段名另外一个是property表示程序类中的对应属性名如果只是在单表进行查询只设置不同字段名的映射就可以了但是如果是多表查询必须将数据表中所有的字段与类中所有的属性生成映射关系。
就像下面这样图中类属性与数据表字段是相同的实际情况可以存在不同的字段名
所以当类中的属性和数据库表中的字段名不一致面对查询结果为null的这种情况有三种解决方案
将类中的属性和数据库表的字段名改成一致的。使用 sql 语句中的 as 进行列名字段名重命名让列名字段名等于属性名即可。就是上面介绍的定义一个 resultMap将属性名和字段名进行手动映射。
5. 多表查询
5.1. 一对一表映射
一对一关系就是对于一个属性只与另外一个属性有关系的映射这就是一对一的关系举个例子对于一篇博客它只会对应到一个用户那么博客与用户的关系就是一对一的关系下面我们尝试在 mybatis 中实现一对一多表联查。
首先我们将数据库中的博客表与 java 程序中的博客类对应起来就是按照数据库中的博客表建立一个类 Data
public class Articleinfo {private int id;private String title;private String content;private LocalDateTime createtime;private LocalDateTime updatetime;private int uid;private int rcount;private int state;// 联表字段private UserInfo userInfo;
}目前文章表中只有一条数据如下图 1️⃣第一步创建Mapper接口和对应的xml文件。 2️⃣第二步在接口中声明方法和在 xml 中写 sql 标签与语句。
// 根据文章 id 获取文章对象, 显示用户信息
public ArticleInfo getArticleById(Param(id) Integer id);
select idgetArticleById resultTypecom.example.demo.model.ArticleInfoselect * from articleinfo where id#{id};
/select3️⃣第三步编写单元测试并执行。 结果如下
由于我们数据表的字段与类的属性名是一致的那些普通的属性都一一对应上了都成功被赋值了但是由于UserInfo类在数据表中没有所以并没有得到 UserInfo 对象。
如果我们想要拿到这个对象我们可以使用resultMap而且 sql 也需要修改想要实现一对一多表查询需要设置多表查询 sql 语句我们使用左外连接进行多表查询
select idgetArticleById resultMapBaseMapselect a.*, u.* from articleinfo as a left join userinfo as u on a.uidu.id where a.id#{id};
/select此外我们除了设置username与Articleinfo类中每个属性与数据表的映射之外我们还要在Articleinfo类对应的resultMap中使用association标签。最少需要设置两个属性一个是property表示在主表Articleinfo中对应副表UserInfo映射对象的变量名另外一个是副表UserInfo对应的resultMap。
Articleinfo类对应的 resultMap
resultMap idBaseMap typecom.example.demo.model.Articleinfoid columnid propertyid/idresult columntitle propertytitle/resultresult columncontent propertycontent/resultresult columncreatetime propertycreatetime/resultresult columnupdatetime propertyupdatetime/resultresult columnuid propertyuid/resultresult columnrcount propertyrcount/resultresult columnstate propertystate/resultassociation propertyuserInfo resultMapcom.example.demo.dao.UserMapper.BaseMap/association
/resultMapUserInfo类对应的 resultMap
resultMap idBaseMap typecom.example.demo.model.UserInfoid columnid propertyid/idresult columnusername propertyusername/resultresult columnpassword propertypassword/resultresult columnphoto propertyphoto/resultresult columncreatetime propertycreatetime/resultresult columnupdatetime propertyupdatetime/resultresult columnstate propertystate/result
/resultMap注意多表查询时要将 resultMap 中的所有字段进行映射如果UserInfo类的 resultMap 没有将所有的属性都与数据库的表映射就会造成获取到的userInfo对象中的数据不完整假设只设置了id与name的映射那就只能获取到的对象id与name的值其他字段是获取不到的。
将两张表的 resultMap 映射好后我们运行同样的单元测试代码结果如下 但此时仍然存在一个问题我们所建的两个表存在名字相同的字段可能会出现数据覆盖的情况如两个表的主键都叫id但是id在两个表的含义是不同的在用户表它表示用户id在文章表它表示文章的id现在我们将获取两表的数据的id改为不相同再来看一看单元测试运行的结果 从正常的逻辑上看由于不存在id为1的用户所以获取到UserInfo对象应该为null才对但是运行的结果却存在UserInfo对象并且与文章表的重名字段都被赋值了文章表中的数据为了解决这个问题我们必须在文章表主表的resultMap中设置属性columnPrefix它的值随便设置作用是识别副表字段时加上一段前缀如我们给用户表的字段加上前缀u_此时 sql 中就不能使用*来一次表示所有元素了需要一个一个单独设置并将字段全部重命名带上u_前缀 。
association字段设置
association propertyuserInfo columnPrefixu_ resultMapcom.example.demo.mapper.UserMapper.BaseMap /associationsql 语句需要将用户表的字段全部重命名
select idgetArticleById resultMapBaseMapselect a.*, u.id as u_id,u.username as u_username,u.password as u_password,u.photo as u_photo,u.createtime as u_createtime,u.updatetime as u_updatetime,u.state as u_statefrom articleinfo as a left join userinfo as u on a.uidu.id where a.id#{id};
/select我们将userInfo对应的用户表的id再改回为1。 再次执行单元测试代码 此时是能够获取到相应的数据的所以如果两个表字段重名了进行多表查询时需要设置columnPrefix属性这样才能够避免不同表同名字段数据覆盖的问题。
所以在创建数据库的数据表时尽量不要让表与表中的字段重名。
除了基于xml实现多表查询还可以基于注解实现使用注解就简单了不少直接设置参数为相应的 sql 即可比如再声明一个getArticleById2方法使用注解实现具体的执行 sql。
// 根据文章 id 获取文章对象, 显示用户信息 (基于注解)
Select(select a.*, u.* from articleinfo as a left join userinfo as u on a.uidu.id where a.id#{id};)
ArticleInfo getArticleById2(Param(id) Integer id);单元测试代码
Test
void getArticleById2() {ArticleInfo articleinfo articleMapper.getArticleById(1);System.out.println(articleinfo);
}结果如下 5.2. 一对多表映射
一对多的关系就是对于一个属性它对映着多个其他的属性比如用户与博客之间的关系一个用户可以对应多篇博客那么用户与博客之间的关系就是一对多的关系我们再尝试使用 mybatis 实现多对多的多表联查。
下面我们以用户表为主文章表为辅来演示如何进行一对多关系的多表查询。
既然是一对多的关系那我们可以在UserInfo类中加上一个储存ArticleInfo对象的List来储存用户发布或所写的文章。
Data
public class UserInfo {private Integer id;private String username;private String password;private String photo;private String createtime;private String updatetime;private Integer state;// 联表字段private ListArticleinfo aList;
}实现多表查询的大致过程如下
在 Mapper 接口中声明方法我们声明一个方法就是通过用户 id 获取用户信息以及对应的文章列表。
// 根据 id 查询用户信息, 显示相应文章信息
UserInfo getUserAndArticlesById(Param(uid) Integer id);在xml文件当中写resultMap映射关系与一对一多表查询不同的是我们需要设置collection标签而不是association标签。在xml文件的resultMap标签中至少设置 resultMap 名字id对应映射的类type等属性里面需要设置数据表与类中所有属性的映射以及设置collection标签需要设置property属性表示需映射的对象名设置 resultMap 即副表的resultMap路径由于你无法保证表与表之间是否存在重名字段需要设置columnPrefix为副表的字段添加上一个前缀防止重名数据覆盖。
collectionpropertyaListresultMapcom.example.demo.mapper.ArticleMapper.BaseMapcolumnPrefixa_
/collection在对应的xml文件当中写 sql 标签以及语句。
select idgetUserAndArticlesById resultMapBaseMapselect u.*, a.id as a_id,a.title as a_title,a.content as a_content,a.createtime as a_createtime,a.updatetime as a_updatetime,a.uid as a_uid,a.rcount as a_rcount,a.state as a_statefrom userinfo as u left join articleinfo as a on u.ida.uid where u.id#{uid}
/select编写单元测试代码验证结果。
Test
void getUserAndArticlesById() {Integer id 1;UserInfo userInfo userMapper.getUserAndArticlesById(id);System.out.println(userInfo);
}结果如下
同样也可以使用注解完成实现起来更简单。
// 根据 id 查询用户信息, 显示相应文章信息 (基于注解)
Select(select u.*, a.* from userinfo as u left join articleinfo as a on u.ida.uid where u.id#{uid};)
UserInfo getUserAndArticlesById2(Param(uid) Integer id);单元测试代码
Test
void getUserAndArticlesById2() {Integer id 1;UserInfo userInfo userMapper.getUserAndArticlesById(id);System.out.println(userInfo);
}结果如下 上面的方式都是对两张表联合后进行的查询是一步实现的其实也可以分步实现比如这里可以先根据uid查询到用户信息再由uid查询到相应文章信息可以使用多线程分别执行这两步效率更高然后将文章信息整合到用户对象中的aList字段中即可。
定义方法这里使用注解实现相应执行 sql。
// 根据用户 id 查询用户信息。
Select(select * from userinfo where id#{id})
UserInfo getUserById2(Param(id)Integer id);// 根据用户 id 查询相应文章信息
Select(select * from articleinfo where uid#{uid})
ListArticleInfo getListByUid(Param(uid)Integer uid);单元测试代码
Test
void getUserList() {int uid 1;// 定义线程池ThreadPoolExecutor threadPool new ThreadPoolExecutor(5, 10,100, TimeUnit.SECONDS,new LinkedBlockingQueue(100));final Object[] resultArray new Object[2];threadPool.submit(new Runnable() {Overridepublic void run() {// 1. 根据 uid 查询 userinforesultArray[0] userMapper.getUserById2(uid);}});threadPool.submit(new Runnable() {Overridepublic void run() {// 2. 根据 uid 查询查询文章列表resultArray[1] articleMapper.getListByUid(uid);}});// 组装数据 (等线程池执行完成之后)while (threadPool.getTaskCount() !threadPool.getCompletedTaskCount()) {}UserInfo userinfo (UserInfo) resultArray[0];userinfo.setAList((ListArticleInfo) resultArray[1]);System.out.println(userinfo);
}执行测试方法结果如下 二. 动态SQL
动态 SQL 是 MyBatis 的强大特性之一了如果你使用过 JDBC 或其它类似的框架你应该能理解根据不同条件拼接 SQL 语句有多痛苦例如拼接时要确保不能忘记添加必要的空格还要注意去掉列表最后一个列名的逗号利用动态 SQL可以彻底摆脱这种痛苦使用动态 SQL 并非一件易事但借助可用于任何 SQL 映射语句中的强大的动态 SQL 语言MyBatis 显著地提升了这一特性的易用性。
之前介绍的 mybatis 增删查改所传的参数都是一定会传入的但是在实际情况中很多参数都是非必传参数使用动态 SQL 就可以解决传入的参数是非必传参数的情况。
动态 SQL 可以解决多余符号的问题如,等。
1. if标签
if标签的作用就是判断一个参数是否有值如果没有值就将对应的参数隐藏。
语法
if test表达式sql
/if
// 例如
if test参数!nullsql部分语句
/if当表达式为真则插入if标签中的sql否则不插入。
我们以在用户表中插入一条记录为例其中插入的字段中头像photo不是必传的参数
方法声明
// 使用动态sql插入数据
int addUser(UserInfo userInfo);动态SQL语句 其中的photo是非必传参数我们使用if标签来判断它是否有值没有值就不插入目标的 sql 语句。
insert idaddUserinsert into userinfo(username, passwordif testphoto!null, photo/if) values(#{username}, #{password}if testphoto!null, #{photo}/if)
/insert单元测试代码
Test
void addUser() {UserInfo userinfo new UserInfo();userinfo.setUsername(zhangsan);userinfo.setPassword(123);int result userMapper.addUser(userinfo);System.out.println(受影响行数: result);
}在单元测试代码中没有给 photo 赋值if 标签会判断它为空不会生成插入 photo 字段的 sql。
结果如下 数据库查询结果 再来试一试给 photo 传值的情况此时生成的 sql 有三个参数
Test
void addUser() {UserInfo userinfo new UserInfo();userinfo.setUsername(lisi);userinfo.setPassword(123);userinfo.setPhoto(default.png);int result userMapper.addUser(userinfo);System.out.println(受影响行数: result);
}结果如下 最终生成的语句多了一个photo参数。
数据库结果 2. trim标签
if标签可以实现非必传参数 sql 的构造在极端情况下有很多个非必传参数此时如果只使用if标签构造出的 sql 语句很有可能会多出一个,因为有很多非必传参数如果只传来一个参数由于不确定后面是否还会有参数因此会预留一个,此时如果没有其他参数就会多出一个,。
而trim标签可以去除 sql 语句前后多余的指定字符它需要搭配if标签使用。
trim标签中有如下属性
prefix表示整个语句块以 prefix 的值作为前缀suffix表示整个语句块以 suffix 的值作为后缀prefixOverrides表示整个语句块要去除掉的前缀suffixOverrides表示整个语句块要去除掉的后缀
语法
trim prefix前缀符, suffix后缀符, prefixOverrides去除多余的前缀字符, suffixOverrides去除多余的后缀字符if test表达式.../if......
/trim假设usernamepasswordphoto都是非必传参数但是至少传递一个我们来写插入语句的动态 SQL。
insert idaddUser2insert into userinfotrim prefix( suffix) prefixOverrides, suffixOverrides,if testusername!nullusername,/ifif testpassword!nullpassword,/ifif testphoto!nullphoto/if/trimvaluestrim prefix( suffix) prefixOverrides, suffixOverrides,if testusername!null#{username},/ifif testpassword!null#{password},/ifif testphoto!null#{photo}/if/trim
/insert单元测试代码
Test
void addUser2() {UserInfo userInfo new UserInfo();userInfo.setUsername(wangwu);userInfo.setPassword(12345622);int result userMapper.addUser(userInfo);System.out.println(受影响行数: result);
}运行结果与生成的 sql 语句
我们发现多余的逗号被自动去除了。 3. where标签
where标签主要是替换where关键字使用可以动态的生成条件如果 sql 中没有使用到where没有查询条件就不会生成where存在查询条件就会生成含有 where 的查询 sql 语句并且可以自动去除前面多余的and。
此时我们 userinfo 表中的数据如下 假设我们可以根据idusernamepassward中的一个或者几个获取用户信息。
select idgetUserListByWhere resultTypecom.example.demo.model.UserInfoselect * from userinfowhereif testid0id#{id}/ifif testusername!nulland username#{username}/ifif testpassword!nulland password#{password}/if/where
/select单元测试代码
这里只传入一个username看是否可以自动删除前面多余的and。
Test
void getListByWhere() {UserInfo userinfo new UserInfo();userinfo.setUsername(wangwu);ListUserInfo list userMapper.getUserListByWhere(userinfo);System.out.println(list);
}结果如下 发现自动生成了where语句并删除了多余的and。 如果我们不设置任何参数查询一个null值就不会生成where语句。 以上where标签也可以使用 trim prefixwhere prefixOverridesand 替换和以下写法效果是一样的。
select idgetUserListByWhere resultTypecom.example.demo.model.UserInfoselect * from userinfotrim prefixwhere prefixOverridesandif testid0id#{id}/ifif testusername!nulland username#{username}/ifif testpassword!nulland password#{password}/if/trim
/select4. set标签
set标签与where标签很相似where用来替换查询SQL而set是用于修改 sql 中用来自动生成set部分的SQL语句。
set标签可以自动去除最后面的一个,。
比如我们写一个能够修改账户名username密码password头像photo的动态 SQL根据id进行修改。
方法声明
// 使用动态SQL实现修改用户信息包括账户名密码头像
int updateUser(UserInfo userInfo);动态 SQL
update idupdateUserupdate userinfosetif testusername!nullusername#{username},/ifif testpassword!nullpassword#{password},/ifif testphoto!nullphoto#{photo}/if/setwhere id#{id}
/update单元测试代码
Test
void updateUser() {UserInfo userinfo new UserInfo();userinfo.setId(3);// 修改密码为 456userinfo.setPassword(456);int result userMapper.updateUser(userinfo);System.out.println(受影响行数 : result);
}结果如下 修改成功并且可以根据传入参数个数自动生成相应的修改 sql以及可以自动去除最后的,。 数据库验证 以上set标签也可以使用 trim prefixset suffixOverrides,替换和以下写法效果是一样的。
update idupdateUserupdate userinfotrim prefixset suffixOverrides,if testusername!nullusername#{username},/ifif testpassword!nullpassword#{password},/ifif testphoto!nullphoto#{photo}/if/trimwhere id#{id}
/update5. foreach标签
对集合进行遍历可以使用foreach标签常用的场景有批量删除功能。
collection绑定方法参数中的集合如 ListSetMap或数组对象item遍历时的每一个对象open语句块开头的字符串close语句块结束的字符串separator每次遍历之间间隔的字符串
为了方便演示批量删除我们随便插入几条数据到数据库 方法声明
// 使用动态 sql 批量删除元素
public int deleteIds(ListInteger ids);动态 SQL 语句
delete iddelByIdsdelete from userinfo where id inforeach collectionids open( close) itemid separator,#{id}/foreach
/delete单元测试代码 删除数据库中id为 8 9 10 11 的用户。
Test
void delByIds() {ListInteger list new ArrayListInteger() {{add(8);add(9);add(10);add(11);}};int result userMapper.delByIds(list);System.out.println(受影响行数 result);
}结果如下 验证数据库成功生成了批量删除的 sql这就是foreach标签的作用它能够遍历集合。