什么是 MyBatis ?如何使用 MyBatis ?
1. MyBatis 是什么?
MyBatis 是一个开源的 Java 持久化框架,它用于简化数据库访问和操作的过程。它提供了一种将数据库操作与 Java 对象之间进行映射的方式,使得开发人员可以使用简单的 XML 配置或注解来定义 SQL 查询、插入、更新和删除等操作。
MyBatis 的核心思想是将 SQL 语句与 Java 代码解耦,通过将 SQL 语句和参数绑定到预定义的查询语句或存储过程中,使得开发人员可以通过调用简单的方法来执行数据库操作,而无需编写冗长的 JDBC 代码。
2. 创建数据库和表
这里先创建一个数据库,以方便后面的测试。
-- 创建数据库
drop database if exists mycnblog;
create database mycnblog DEFAULT CHARACTER SET utf8mb4;
-- 使用数据数据
use mycnblog;
-- 创建表[用户表]
drop table if exists userinfo;
create table userinfo(
id int primary key auto_increment,
username varchar(100) not null,
password varchar(32) not null,
photo varchar(500) default '',
createtime timestamp default current_timestamp,
updatetime timestamp default current_timestamp,
`state` int default 1
) default charset 'utf8mb4';
-- 创建文章表
drop table if exists articleinfo;
create table articleinfo(
id int primary key auto_increment,
title varchar(100) not null,
content text not null,
createtime timestamp default current_timestamp,
updatetime timestamp default current_timestamp,
uid int not null,
rcount int not null default 1,
`state` int default 1
)default charset 'utf8mb4';
-- 创建视频表
drop table if exists videoinfo;
create table videoinfo(
vid int primary key,
`title` varchar(250),
`url` varchar(1000),
createtime timestamp default current_timestamp,
updatetime timestamp default current_timestamp,
uid int
)default charset 'utf8mb4';
-- 添加用户信息
INSERT INTO `mycnblog`.`userinfo` (`id`, `username`, `password`, `photo`, `createtime`, `updatetime`, `state`) VALUES
(1, 'admin', 'admin', '', '2021-12-06 17:10:48', '2021-12-06 17:10:48', 1),(2,'小明','12345','','2022-12-06 17:10:48', '2022-12-06 17:10:48', 1);
-- 文章添加测试数据
insert into articleinfo(title,content,uid)
values('Java','Java正文',1);
-- 添加视频
insert into videoinfo(vid,title,url,uid) values(1,'java title','http://www.baidu.com',1);
3. 添加 MyBatis 框架
3.1 创建新项目
在创建 Spring Boot 的时候,添加MyBatis Framework
和MySQL Driver
:
3.2 在老项目上添加
当在老项目上添加时,这就要借助EditStarters
插件(IDEA插件商店下载),下载过后在pom.xml
中鼠标右键:
3.3 配置连接
3.3.1 配置连接字符串
在application.properties
中配置如下信息:
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/mycnblog?characterEncoding=utf8&useSSL=false
spring.datasource.username=root
# 设置密码
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# 设置 Mybatis 的 xml 保存路径
mybatis.mapper-locations=classpath:/mybatis/*Mapper.xml
在 MyBatis 中,Mapper 文件用于定义数据库操作的 SQL 语句和映射关系。每个 Mapper 文件通常对应一个数据访问接口(DAO 接口),其中包含了一组与数据库相关的方法。
mybatis.mapper-locations=classpath:/mybatis/*Mapper.xml
的意思是告诉 MyBatis 在类路径下的 mybatis
目录中查找所有以 Mapper.xml
结尾的文件作为 Mapper 文件。classpath:
表示在类路径下搜索,/mybatis/
表示 mybatis
目录的路径,*Mapper.xml
表示匹配任意名称以 Mapper.xml
结尾的文件。
然后打开数据库服务,之后开始运行程序,如果没有报错就算成功(废话)。
4. 使用 MyBatis
4.1 使用前准备
一般是用下⾯的流程来实现 MyBatis 操作的。
- 在
resources
下创建mybatis
目录。
- 创建一个实体类
UserEntity
以及一个Mapper
接口。
@Data //自动生成set、get、toString
public class UserEntity {
//与表的属性一一对应
private Integer id;
private String username;
private String password;
private String photo;
private LocalDateTime createtime;
private LocalDateTime updatetime;
private Integer state;
}
@Mapper //表示
public interface UserMapper {
//查询所用的 User
List<UserEntity> getAll();
}
@Mapper
注解用于在 MyBatis 中标识一个接口类作为 Mapper 接口。Mapper 接口是定义数据库操作的接口,通过该接口可以调用 MyBatis 提供的 SQL 查询、插入、更新和删除等操作。
使用 @Mapper
注解标注接口后,MyBatis 将会自动为该接口生成实现类,并将其注册到 MyBatis 的上下文中,从而可以在应用程序中直接使用该接口进行数据库操作。
- 添加
.xml
文件
填如下代码:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.UserMapper">
</mapper>
namespace
属性的值应该与对应的 Mapper 接口的全限定名(包括包路径)一致。通过指定 namespace
,MyBatis 将会把该 Mapper XML 文件与对应的 Mapper 接口关联起来。
这样,MyBatis 将会将该 Mapper XML 文件与 com.example.demo.mapper.UserMapper
接口关联起来,使得接口中的方法可以与 XML 文件中定义的 SQL 语句进行映射。
- 写
SQL
语句
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.UserMapper">
<select id="getAll" resultType="com.example.demo.entity.UserEntity">
select * from userinfo
</select>
</mapper>
resultType
用于指定 SQL 查询结果的类型。它定义了查询结果应该映射到的实体类或其他数据类型。
<select>
查询标签:是用来执行数据库的查询操作的
id
:是和 Interface(接口)中定义的方法名称⼀样的,表示对接口的具体实现方法。
- 按照分层来查询数据
@Service
public class UserService {
@Autowired //注入对像,注意这里是userMapper接口的代理对象
private UserMapper userMapper;
public List<UserEntity> getAll(){
//这个方法在 .xml 文件中已经实现了
return userMapper.getAll();
}
}
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired //注入 UserService 对象
private UserService userService;
@RequestMapping("/getall")
public List<UserEntity> getAll(){
return userService.getAll();
}
}
Mapper 接口的实现类是由 MyBatis 在运行时动态生成的。该实现类会根据 Mapper 接口中定义的方法及相应的 SQL 映射关系,自动生成对应的数据库操作代码。
4.2 通过参数来查询数据
加一个方法:传入一个id
,通过id
来查询User
:
@Mapper
public interface UserMapper {
//查询所用的 User
List<UserEntity> getAll();
//根据 id 查询用户对象
UserEntity getUserById(@Param("id") Integer id);
}
在UserMapper.xml
中:
${}
匹配传进来的参数,${}
是一种占位符语法,用于在 SQL 语句中动态地替换参数。
@Param()
注解用于指定方法参数的名称(就是重命名),并在SQL语句中引用这些参数。它的主要作用是为了解决方法中存在多个参数时,MyBatis无法确定参数的名称而导致的问题。
(1)通过 url 来测试
在UserService、UserController
中添加方法:
(2)通过单元测试的方式
添加后,补充代码(就当作 main 方法):
@SpringBootTest //表示当前单元测试类是运行在 Spring Boot 环境中的
class UserMapperTest {
@Autowired
private UserMapper userMapper;
@Test
void getUserById() {
UserEntity user = userMapper.getUserById(1);
System.out.println(user);
}
}
运行结果:
4.3 占位符 ${} 与 #{}
${}
与 #{}
都是用于占位的特殊符号,为了演示它们的区别,这里需要输出执行的 SQL 语句,要想输出 SQL 语句还要得进行一些配置:
在application.properties
配置文件中添加如下代码:
# 打印 MyBatis 执行的 SQL 语句
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
# 设置日志等级
logging.level.com.example.demo=debug
-
${}
占位符:${}
是用于动态拼接查询语句中的参数占位符。${}
会将传入的值直接插入到查询语句中,不会进行安全处理。${}
占位符主要用于拼接静态的SQL语句片段或引用已定义的变量或属性。
这里还是执行上面单元测试中的getUserById()
方法。
-
#{}
占位符:#{}
是用于预编译查询语句中的参数占位符。#{}
会将传入的值进行安全处理,防止SQL注入攻击。#{}
占位符会将参数值转义并插入到查询语句中,使用占位符时,参数值会被当做预编译语句的参数传递给数据库,从而提供更好的安全性。
4.3.1 ${} 与 #{} 替换字符串的情况
添加一个方法:通过名字来查询用户。
(1) ${} 替换字符串
UserMapper:
//同过姓名来查询 用户
UserEntity getUserByName(@Param("username") String name);
UserMapper.xml:
<select id="getUserByName" resultType="com.example.demo.entity.UserEntity" >
select * from userinfo where username=${username}
</select>
UserMapperTest:
@Test
void getUserByName() {
UserEntity user = userMapper.getUserByName("小明");
System.out.println(user);
}
结果:报错了
解决办法:
- 方法一:
@Test
void getUserByName() {
UserEntity user = userMapper.getUserByName("\'小明\'");
System.out.println(user);
}
- 方法二:
<select id="getUserByName" resultType="com.example.demo.entity.UserEntity" >
select * from userinfo where username='${username}'
</select>
(2) #{} 替换字符串
<select id="getUserByName" resultType="com.example.demo.entity.UserEntity" >
select * from userinfo where username=#{username}
</select>
结果:成功
4.3.2 SQL 注入
${}
可能会被 SQL
注入,#{}
则是防止SQL注入攻击,这里演示一下什么是SQL
注入。
添加一个方法:登录,传入一个对象,判断用户名与密码是否正确。
UserMapper:
//登录的方法
UserEntity login(UserEntity user);
UserMapper.xml:
<select id="login" resultType="com.example.demo.entity.UserEntity">
select * from userinfo where username = '${username}' and password = '${password}'
</select>
username = '${username}'
中的username
属性框架已经自动加载好了。(user.username
在这里是错误的)
UserMapperTest:
@Test
void login() {
UserEntity user = new UserEntity();
user.setUsername("小明");
user.setPassword("");//不知道密码
UserEntity user2 = userMapper.login(user);
System.out.println(user2);
}
如果我只知道“小明”这个用户名,不知道密码,可以用SQL注入的方式获取所用的数据:
(ps:本博客的目的是为了提供学习和研究的参考,介绍了 SQL 注入等安全漏洞和攻击的概念。请注意,这些内容仅供学习和了解安全领域的知识,不应用于非法活动或恶意目的。)
@Test
void login() {
UserEntity user = new UserEntity();
user.setUsername("' OR username = '小明' LIMIT 1 -- ");
user.setPassword("");
UserEntity user2 = userMapper.login(user);
System.out.println(user2);
}
我们只需要将用户名输入为:' OR username = '小明' LIMIT 1 --
,就能获取到数据。为什么呢?
SELECT * FROM userinfo WHERE username = '' OR username = '小明' LIMIT 1 -- ' AND password = ''
这个查询语句中的 LIMIT 1
限制了结果集只返回一条记录,而注释符号 --
则注释掉了原本的密码验证部分。
结果:
(1)预防 SQL 注入
使用 ${}
易被 SQL 注入,而 #{}
则可以避免SQL
注入:
同样的代码,改一下.xml
里的${}
<select id="login" resultType="com.example.demo.entity.UserEntity">
select * from userinfo where username = #{username} and password = #{password}
</select>
结果:
查找失败,返回null
。
4.3.3 ${} 的优点
我们想排序的时候,${}
就发挥它的优势了,就比如下面的场景:
select * from userinfo order by id ${sort}
使⽤ ${sort}
可以实现排序查询,而使用#{sort}
就不能实现排序查询了,因为当使用 #{sort}
查询时,如果传递的值为 String
则会加单引号,就会导致sql
错误。
4.4 增、删、改操作
4.4.1 修改数据
修改密码:
//修改密码
Integer updatePassword(@Param("id") Integer id,@Param("passWord") String passWord,@Param("newPassWord") String newPassWord);
<update id="updatePassword">
update userinfo set password=#{newPassWord}
where id=#{id} and password=#{passWord}
</update>
返回的值为修改的行数。
@Transactional //事务,不会污染数据库
@Test
void updatePassword() {
Integer n = userMapper.updatePassword(2,"12345","000000");
System.out.println("修改的行数:" + n);
}
结果:
4.4.2 删除数据
删除用户:
//删除用户
Integer dalById(@Param("id") Integer id);
<delete id="dalById">
delete from userinfo where id = #{id}
</delete>
@Transactional
@Test
void dalById() {
Integer n = userMapper.dalById(1);
System.out.println("删除的行数:" + n);
}
当一个方法被标记为 @Transactional
时,Spring 框架会在方法开始之前启动一个事务,并在方法结束时自动提交或回滚事务。
4.4.3 增加数据
(1)返回添加的行数
//添加用户
Integer addUser(UserEntity user);
<insert id="addUser">
insert into userinfo(username,password) values(#{username},#{password})
</insert>
@Test
void addUser() {
UserEntity user = new UserEntity();
user.setUsername("王五");
user.setPassword("12346789");
Integer n = userMapper.addUser(user);
System.out.println("添加的行数:" + n);
System.out.println(user);
}
(2)返回自动生成的主键
//添加用户,返回自增的主键
Integer addUserGetKey(UserEntity user);
<insert id="addUserGetKey" useGeneratedKeys="true" keyProperty="id">
insert into userinfo(username,password) values(#{username},#{password})
</insert>
@Test
void addUserGetKey() {
UserEntity user = new UserEntity();
user.setUsername("李四");
user.setPassword("12346789");
Integer n = userMapper.addUserGetKey(user);
System.out.println("变动的行数:" + n);
System.out.println(user);
}
useGeneratedKeys
是一个布尔类型的属性,用于指示是否使用自动生成的主键
,keyProperty
是一个字符串类型的属性,用于指定主键值应该存储在哪个Java对象的属性中。
4.5 like 模糊查询
//like 模糊查询
List<UserEntity> getListByName(@Param("username") String username);
<select id="getListByName" resultType="com.example.demo.entity.UserEntity">
select * from userinfo where username like '%#{username}%'
</select>
@Test
void getListByName() {
String username = "张";
List<UserEntity> list = userMapper.getListByName(username);
for(UserEntity user:list){
System.out.println(user);
}
}
结果:报错了
为什么报错了呢?因为${}
占位符会自动加单引号,就变为了:like '%'username'%'
;
修改后的代码:
<select id="getListByName" resultType="com.example.demo.entity.UserEntity">
select * from userinfo where username like concat('%',#{username},'%')
</select>
在MySQL中,使用CONCAT
函数可以将两个或多个字符串连接在一起。
4.6 返回类型 resultMap
如果实体类与数据库表中的属性不一致的时候,用resultType
类型就行不通了:
<select id="getUserById" resultType="com.example.demo.entity.UserEntity">
select * from userinfo where id = #{id}
</select>
@Test
void getUserById() {
UserEntity user = userMapper.getUserById(1);
System.out.println(user);
}
结果:
发现name
映射不到对象的属性上,这时候有两种方法可以解决:
(1)直接在 SQL 中换名:
<select id="getUserById" resultType="com.example.demo.entity.UserEntity">
select id, username as name, password, photo, ...... from userinfo where id = #{id}
</select>
(2) 使用resultMap:
<resultMap id="BaseMap" type="com.example.demo.entity.UserEntity">
<!-- 标识主键的映射 -->
<id property="id" column="id"></id>
<!-- 其它属性的映射 -->
<result property="name" column="username"></result>
<result property="password" column="password"></result>
<result property="createtime" column="createtime"></result>
<result property="updatetime" column="updatetime"></result>
<result property="photo" column="photo"></result>
<result property="state" column="state"></result>
</resultMap>
<select id="getUserById" resultMap="BaseMap">
select * from userinfo where id = #{id}
</select>
<resultMap>
元素用于定义查询结果的映射规则,将查询结果映射到Java对象上。下面是对<resultMap>
元素的解释:
id
: 指定了结果映射的唯一标识符,这里是"BaseMap"。type
: 指定了映射结果的Java对象类型,这里是com.example.demo.entity.UserEntity
。在这个示例中,查询结果将映射到UserEntity
类的实例上。
//根据 id 查询用户对象
UserEntity getUserById(@Param("id") Integer id);
@Test
void getUserById() {
UserEntity user = userMapper.getUserById(1);
System.out.println(user);
}
结果:
(后面把name改回username)
4.6 多表查询
我们的数据库表有:
userinfo:
articleinfo:
这时候我想查询Java
这篇文章,要求要有作者的名字,这时候得多表连接查询了。
- 创建实体类以及对应的接口
在 MyBatis 中,VO 是指值对象(Value Object)。值对象是一种轻量级的对象,用于封装多个属性或字段的数据。它主要用于数据传输和封装,而不包含业务逻辑。在 MyBatis 中, VO 通常用于封装查询结果或传递参数。 下面就是用来返回多表查询的结果的。
@Data
public class ArticleInfo {
private Integer id;
private String title;
private String content;
private LocalDateTime createtime;
private LocalDateTime updatetime;
private Integer uid;
private Integer rcount;
private Integer state;
}
@Data
public class ArticleInfoVO extends ArticleInfo {
private String username;
@Override
public String toString() {
return "ArticleInfoVO{" +
"username='" + username + '\'' +
"} " + super.toString();
}
}
@Mapper
public interface ArticleMapper {
//查询文章详情
ArticleInfoVO getDetail(@Param("id") Integer id);
}
- 配置 .xml 文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.ArticleMapper">
<select id="getDetail" resultType="com.example.demo.entity.vo.ArticleInfoVO">
select a.*,u.username from articleinfo a
left join userinfo u on u.id = a.uid
where a.id = #{id}
</select>
</mapper>
- 单元测试
@SpringBootTest
class ArticleMapperTest {
@Autowired
private ArticleMapper articleMapper;
@Test
void getDetail() {
ArticleInfoVO articleInfoVO = articleMapper.getDetail(1);
System.out.println(articleInfoVO);
}
}
结果:
5. 动态 SQL
动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。—— mybatis – MyBatis 3 | 动态 SQL
用一句话总结就是在 .xml 中用判断语句。
5.1 <if> 标签
在注册⽤户的时候,可能会有这样⼀个问题:
注册分为两种字段:必填字段和⾮必填字段,那如果在添加用户的时候有不确定的字段传入,程序应该如何实现呢?
//添加用户
Integer addUser2(UserEntity user);
<insert id="addUser2">
insert into userinfo(
username,
password
<if test="photo!=null and photo !='' ">
,photo
</if>
) values(
#{username},
#{password}
<if test="photo != null and photo !='' ">
,#{photo}
</if>
)
</insert>
@Transactional
@Test
void addUser2() {
String username = "老王";
String password = "123456";
UserEntity user = new UserEntity();
user.setUsername(username);注意 test 中的 sex,是传⼊对象中的属性,不是数据库字段
user.setPassword(password);
int result = userMapper.addUser2(user);
System.out.println("添加:" + result);
}
如果条件为真,则 <if>
标签包含的 SQL 语句将被执行(拼接);否则,将被忽略。
注意 test 中的 sex,是传入对象中的属性,不是数据库字段。
5.2 <trim> 标签
虽然用<if>
可以解决很多问题,但是它也有局限性,比如极端情况下,当所有的属性都是非必填的时候,xml
中SQL
语句的逗号不好处理,这时候用 <trim>
就比较方便了。
<trim>
标签是用于处理 SQL
查询语句中多余空白和动态拼接的元素之一。它可以用来去除不必要的空白字符,并在需要时动态添加或移除 SQL
语句的部分。
//添加用户
Integer addUser3(UserEntity user);
<insert id="addUser3">
insert into userinfo
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="username != null and username != '' ">
username,
</if>
<if test="password != null and password != '' ">
password,
</if>
<if test="photo != null and photo != '' ">
photo,
</if>
</trim>
<trim prefix="values(" suffix=")" suffixOverrides=",">
<if test="username != null and username != '' ">
#{username},
</if>
<if test="password != null and password != '' ">
#{password},
</if>
<if test="photo != null and photo != '' ">
#{photo},
</if>
</trim>
</insert>
@Transactional
@Test
void addUser3() {
String username = "老王";
String password = "123456";
UserEntity user = new UserEntity();
user.setUsername(username);
user.setPassword(password);
int result = userMapper.addUser2(user);
System.out.println("添加:" + result);
}
<trim>
标签具有以下属性:
prefix
:在<trim>
标签包含的 SQL 语句之前添加的字符串。suffix
:在<trim>
标签包含的 SQL 语句之后添加的字符串。prefixOverrides
:如果 SQL 语句以指定的字符串开头,则将其从 SQL 语句中移除。suffixOverrides
:如果 SQL 语句以指定的字符串结尾,则将其从 SQL 语句中移除。
结果:
5.3 <where> 标签
有如下场景:根据 id 或 文章名(title)来查询文章,如果都为空则返回全部文章,并且id、title都是非必传的,这里需要处理xml
中的where
。
//通过 id 或 标题 来查询文章
List<ArticleInfoVO> getListByIdOrTitle(@Param("id") Integer id, @Param("title") String title);
@Test
void getListByIdOrTitle() {
List<ArticleInfoVO> list = articleMapper.getListByIdOrTitle(null,"Java");
System.out.println(list.size());
}
对于 .xml 文件中的写法有三种:
- 第一种:
<select id="getListByIdOrTitle" resultType="com.example.demo.entity.vo.ArticleInfoVO">
select * from articleinfo
where 1=1
<if test="id!=null and id>0">
and id=#{id}
</if>
<if test="title!=null and title!='' ">
and title like concat('%',#{title},'%')
</if>
</select>
结果:
- 第二种:
<select id="getListByIdOrTitle" resultType="com.example.demo.entity.vo.ArticleInfoVO">
select * from articleinfo
<trim ="where" suffixOverrides="and">
<if test="id!=null and id>0">
id=#{id} and
</if>
<if test="title!=null and title!='' ">
title like concat('%',#{title},'%')
</if>
</trim>
</select>
当id
与title
都为null
的时候,refix
不会执行。
- 第三种:<where>标签
<select id="getListByIdOrTitle" resultType="com.example.demo.entity.vo.ArticleInfoVO">
select * from articleinfo
<where>
<if test="id!=null and id>0">
id=#{id}
</if>
<if test="title!=null and title!='' ">
and title like concat('%',#{title},'%')
</if>
</where>
</select>
<where>
标签,当有至少一个条件发生的时候,它会自动生成 where
;并且,<where>
标签会自动移除前导的(后导不行)连接符(and 或者 or),确保生成的SQL语句语法正确。
结果:
5.4 <set>标签
根据传入的用户对象属性来更新用户数据:
//根据传入的用户对象属性来更新用户数据
Integer update(UserEntity user);
<update id="update">
update userinfo
<set>
id = #{id},
<if test="username!=null">
username=#{username},
</if>
<if test="password!=null">
password=#{password},
</if>
<if test="photo!=null">
photo=#{photo},
</if>
<if test="createtime!=null">
createtime=#{createtime},
</if>
<if test="updatetime!=null">
updatetime=#{updatetime},
</if>
<if test="state!=null">
state=#{state}
</if>
</set>
where id = #{id}
</update>
@Transactional
@Test
void update() {
UserEntity user = new UserEntity();
user.setId(2);
user.setPassword("99999");
user.setUsername("小明");
userMapper.update(user);
}
结果:
<set>
与<where>
是一样的,可以自动添加<set>
,以上<set>
标签也可以使用 <trim prefix="set" suffixOverrides=",">
替换,需要注意的是<set>
移除的是后导逗号。
5.5 <foreach>标签
对集合进行遍历时可以使用该标签。<foreach>
标签有如下属性:
collection
:指定要迭代的集合或数组的表达式。item
:遍历时的每⼀个对象。open
:循环开始时生成的字符串。close
:循环结束时生成的字符串。separator
:每次循环迭代之间生成的分隔符。
//根据文章id集合批量删除文章
Integer delByIdList(List<Integer> list);
<delete id="delByIdList">
delete from articleinfo
where id in
<foreach collection="list" open="(" close=")" item="userid" separator=",">
#{userid}
</foreach>
</delete>
@Transactional
@Test
void delByIdList() {
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
articleMapper.delByIdList(list);
}
在批量删除数据的时候可以使用这个。
转载自:https://juejin.cn/post/7234821431803871293