文章目录
- [1. 动态SQL](#1. 动态SQL)
-
- [1.1 <if>标签](#1.1
标签) - [1.2 <trim>标签](#1.2
标签) - [1.3 <where>标签](#1.3
标签) - [1.4 <set>标签](#1.4
标签) - [1.5 <foreach>标签](#1.5
标签) - [1.6 <include>标签](#1.6
标签)
- [1.1 <if>标签](#1.1
- [2. MyBatis-Plus](#2. MyBatis-Plus)
- [3. MyBatis-Plus复杂操作](#3. MyBatis-Plus复杂操作)
-
- [3.1 常见注解](#3.1 常见注解)
-
- [3.1.1 @TableName](#3.1.1 @TableName)
- [3.1.2 @TableField](#3.1.2 @TableField)
- [3.1.3 @TableId](#3.1.3 @TableId)
- [3.2 打印日志](#3.2 打印日志)
- [3.3 条件构造器](#3.3 条件构造器)
-
- [3.3.1 QueryWrapper](#3.3.1 QueryWrapper)
- [3.3.2 UpdateWrapper](#3.3.2 UpdateWrapper)
-
- [3.3.3 LambdaQueryWrapper](#3.3.3 LambdaQueryWrapper)
- [3.3.4 LambdaUpdateWrapper](#3.3.4 LambdaUpdateWrapper)
1. 动态SQL
动态 SQL 是Mybatis的强大特性之一,能够完成不同条件下不同的 sql 拼接
1.1 <if>标签
可以用if标签来实现非必填字段
比如性别 gender 为非必填字段
接口定义
java
Integer insertUserByCondition(Integer gender);
Xml实现
xml
<insert id="insertUserByCondition">
INSERT into userinfo (
username, password, age,
<if test="gender != null">
gender,
</if>
phone)
VALUES (
#{username}, #{password}, #{age},
<if test="gender != null">
#{gender},
</if>
#{phone})
</insert>
或者使用注解方式, 但并不推荐, 感兴趣可自行了解
test标签中的gender字段, 是传入对象的属性, 要跟Java对象的属性 对应, 而不是数据库的字段
不可以不判断直接把字段设置为null, 这种情况下, 如果gender字段有默认值, 就会设置为默认值
1.2 <trim>标签
之前的插入用户功能,只是有一个 gender 字段可能是选填项,如果有多个字段,一般考虑使用标签结合标签,对多个字段都采取动态生成的方式.
- prefix:表示整个语句块,以prefix的值作为前缀
- suffix:表示整个语句块,以suffix的值作为后缀
- prefixOverrides:表示整个语句块要去除掉的前缀
- suffixOverrides:表示整个语句块要去除掉的后缀
xml
<insert id="insertUserByCondition">
INSERT INTO userinfo
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="username !=null">
username,
</if>
<if test="password !=null">
`password`,
</if>
<if test="age != null">
age,
</if>
<if test="gender != null">
gender,
</if>
<if test="phone != null">
phone,
</if>
</trim>
VALUES
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="username !=null">
#{username},
</if>
<if test="password !=null">
#{password},
</if>
<if test="age != null">
#{age},
</if>
<if test="gender != null">
#{gender},
</if>
<if test="phone != null">
#{phone}
</if>
</trim>
</insert>
以上的sql,
基于 prefix 配置,开始部分加上 (
基于 suffix 配置,结束部分加上 )
多个 组织的语句都以 , 结尾,在最后拼接好的字符串还会以 , 结尾,会基于suffixOverrides 配置去掉最后一个, 注意suffixOverrides 只能去掉末尾的逗号, 如果末尾不是, suffixOverrides不会做处理
1.3 <where>标签
需求: 传入的用户对象,根据属性做where条件查询,用户对象中属性不为 null 的,都为查询条件.
如:
username 为 "a",则查询条件为 where username="a"
每个条件前面都写 AND,where标签会自动去掉第一个 AND,其他的保留
java
List<UserInfo> queryByCondition(UserInfo userInfo);
xml
<select id="queryByCondition" resultType="com.example.test.demos.dao.UserInfo">
select id, username, age, gender, phone, delete_flag, create_time,
update_time
from userinfo
<where>
<if test="age != null">
and age = #{age}
</if>
<if test="gender != null">
and gender = #{gender}
</if>
<if test="deleteFlag != null">
and delete_flag = #{deleteFlag}
</if>
</where>
</select>
注意语句中数据库字段和Java对象字段的映射关系
1.4 <set>标签
需求: 根据传入的用户对象属性来更新用户数据,可以使用标签来指定动态内容
接口定义: 根据传入的用户 id 属性,修改其他不为 null 的属性
java
Integer updateUserByCondition(UserInfo userInfo);
xml
<update id="updateUserByCondition">
<set>
<if test="username != null">
username = #{username}
</if>
<if test="age != null">
age = #{age},
</if>
<if test="deleteFlag != null">
delete_flag = #{deleteFlag},
</if>
</set>
where id = #{id}
</update>
<set> :动态的在SQL语句中插入set关键字,并会删掉额外的逗号. (用于update语句中) 以上标签也可以使用 <trim prefix="set" suffixOverrides=","> 替换
用if标签也能实现, 但是需要处理边界情况和多出来逗号, 很麻烦
1.5 <foreach>标签
对集合进行遍历时可以使用该标签。标签有如下属性:
- collection:绑定方法参数中的集合,如 List,Set,Map或数组对象
- item:遍历时的每一个对象
- open:语句块开头的字符串
- close:语句块结束的字符串
- separator:每次遍历之间间隔的字符串
接口定义: 根据多个userid, 删除用户数据
java
void deleteByIds(@Param("ids") List<Integer> ids);
XML实现
xml
<delete id="deleteByIds">
delete from userinfo
where id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</delete>
形参如果是多个集合, 需要用@Param, 否则会报错, 用了@Param后, collection的值要和@param中写的一致
1.6 <include>标签
在xml映射文件中配置的SQL,有时可能会存在很多重复的片段,此时就会存在很多冗余的代码
我们可以对重复的代码片段进行抽取,将其通过 <sql> 标签封装到一个SQL片段,然后再通过<include> 标签进行引用
<sql> :定义可重用的SQL片段
<include> :通过属性refid,指定包含的SQL片段
通过<sql>定义重复SQL片段
xml
<sql id="allColumn">
id, username, age, gender, phone, delete_flag, create_time, update_time
</sql>
通过 <include> 标签在原来抽取的地方进行引用
xml
<select id="queryAllUser" resultMap="BaseMap">
select
<include refid="allColumn"></include>
from userinfo
</select>
2. MyBatis-Plus
MyBatis-Plus(简称 MP) 是一个 MyBatis 的增强工具, 在 MyBatis 的基础上只做增强不做改变, 为简化开发. 提高效率而生。
MybatisPlus提供了一个基础的 BaseMapper 接口,已经实现了单表的CRUD, 我们自定义的Mapper只需要继承这个BaseMapper, 就无需自己实现单表CRUD
java
@Mapper
public interface UserInfoMapper extends BaseMapper<UserInfo> {}
UserInfoMapper 在继承 BaseMapper 时, 指定了一个泛型, 这个UserInfo就是与数据库表相对应的实体类
默认情况下:
- 表名: 实体类的驼峰表示法转换成蛇形表示法(下划线分割), 作为表名. 比如UserInfo -> user_info
- 字段: 根据实体类的属性名, 转换为蛇形表示法作为字段名. 比如deleteFlag -> delete_flag
- 主键: 默认为id

3. MyBatis-Plus复杂操作
3.1 常见注解
3.1.1 @TableName
我们可以通过 @TableName 来标识实体类对应的表
java
@Data
@TableName("user_info")
public class UserInfo {
private Integer id;
private String username;
private String password;
private Integer age;
private Integer gender;
private String phone;
private Integer deleteFlag;
private Date createTime;
private Date updateTime;
}
3.1.2 @TableField
我们可以通过 @TableField 来标识 对应的字段名
java
@Data
@TableName("user_info")
public class UserInfo {
private Integer id;
private String username;
private String password;
private Integer age;
private Integer gender;
private String phone;
@TableField("delete_flag")
private Integer deleteFlag;
private Date createTime;
private Date updateTime;
}
3.1.3 @TableId
我们可以通过 @TableId 来 指定对应的主键
java
@Data
@TableName("user_info")
public class UserInfo {
@TableId("id")
private Integer id;
private String username;
private String password;
private Integer age;
private Integer gender;
private String phone;
@TableField("delete_flag")
private Integer deleteFlag;
private Date createTime;
private Date updateTime;
}
如果属性名和字段名不一致, 需要在 @TableId 指明对应的字段名;
属性名和字段一致的情况下, 直接加 @TableId 注解就可以
3.2 打印日志
Mybatis-Plus配置日志如下:
yaml
mybatis-plus:
configuration: # 配置打印 MyBatis日志
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
3.3 条件构造器
入门程序里的使用, 都是简单的CRUD, 在实际的应用场景中, 我们还需要使用更复杂的操作, MyBatisPlus 也给我们提供了相应的支持, MyBatis-Plus 提供了一套强大的条件构造器(Wrapper) , 用于构建复杂的数据库查询条件. Wrapper 类 允许开发者以链式调用的方式构造查询条件, 无需编写繁琐的 SQL 语句, 从而提高开发效率并减少 SQL注入 的风险
以下是主要的 Wrapper 类及其功能:
- AbstractWrapper:这是一个抽象基类, 提供了所有 Wrapper 类共有的方法和属性.
- QueryWrapper:用于构造查询条件, 在AbstractWrapper的基础上拓展了一个select方法, 允许指定查询字段
- UpdateWrapper: 用于构造更新条件, 可以在更新数据时指定条件
- LambdaQueryWrapper :基于 Lambda 表达式的查询条件构造器, 它通过 Lambda 表达式来引用实体类的属性,从而避免了硬编码字段名
- LambdaUpdateWrapper: 基于 Lambda 表达式的更新条件构造器, 它允许你使用 Lambda 表达式来指定更新字段和条件,同样避免了硬编码字段名的问题
LambdaQueryWrapper和LambdaUpdateWrapper是能完全替代QueryWrapper和UpdateWrapper的, 而且也更推荐使 lambdaWrapper
3.3.1 QueryWrapper
QueryWrapper并不只用于查询语句, 无论是修改, 删除, 查询, 都可以使用QueryWrapper来构建查询条件这里只简单介绍查询和更新
sql
SELECT id,username,password,age FROM user_info WHERE age = 18 AND username "%min%"
java
void testQueryWrapper() {
QueryWrapper<UserInfo> userInfoQueryWrapper = new QueryWrapper<UserInfo>()
.select("id", "username", "password", "age")
.eq("age", 18)
.like("username", "min");
List<UserInfo> userInfos = userInfoMapper.selectList(userInfoQueryWrapper);
}
QueryWrapper并不只用于查询语句, 无论是修改, 删除, 查询, 都可以使用QueryWrapper来构建查询条件这里只简单介绍查询和更新
sql
UPDATE user_info SET delete_flag=? WHERE age < 20
java
void testUpdateByQueryWrapper(){
QueryWrapper<UserInfo> userInfoQueryWrapper = new QueryWrapper<UserInfo>()
.lt("age", 20);
UserInfo userInfo = new UserInfo();
userInfo.setDeleteFlag(1);
userInfoMapper.update(userInfo, userInfoQueryWrapper);
}
• lt : "less than" 的缩写,表示小于.
• le : "less than or equal to"的缩写, 表示小于等于
• ge : "greater than or equal to" 的缩写, 表示大于等于.
• gt : "greater than" 的缩写, 表示大于.
• eq : "equals" 的缩写, 表示等于.
• ne : "not equals" 的缩写, 表示不等于.
3.3.2 UpdateWrapper
对于更新, 我们也可以直接使用 UpdateWrapper, 在不创建实体对象的情况下, 直接设置更新字段和条件
sql
UPDATE user_info SET delete_flag=0, age=5 WHERE id IN (1,2,3)
java
void testUpdateByUpdateWrapper(){
UpdateWrapper<UserInfo> updateWrapper = new UpdateWrapper<UserInfo>()
.set("delete_flag",0)
.set("age", 5)
.in("id", List.of(1,2,3));
userInfoMapper.update(updateWrapper);
}
QueryWrapper 和 UpdateWrapper存在一个问题, 就是需要写死字段名, 如果字段名发生变更, 可能会因为测试不到位导致出错;
MyBatis-Plus 给我们提供了一种基于Lambda表达式的条件构造器, 它通过 Lambda 表达式来引用实体类的属性,从而避免了硬编码字段名, 也提高了代码的可读性和可维护性
3.3.3 LambdaQueryWrapper
java
void testLambdaQueryWrapper() {
QueryWrapper<UserInfo> queryWrapper = new QueryWrapper<UserInfo>();
queryWrapper.lambda()
.select(UserInfo::getUsername, UserInfo::getPassword, UserInfo::getAge)
.eq(UserInfo::getId, 1);
userInfoMapper.selectList(queryWrapper);
}
3.3.4 LambdaUpdateWrapper
java
void testLambdaUpdateWrapper() {
UpdateWrapper<UserInfo> updateWrapper = new UpdateWrapper<UserInfo>();
updateWrapper.lambda()
.set(UserInfo::getDeleteFlag, 0)
.set(UserInfo::getAge, 5)
.in(UserInfo::getId, List.of(1,2,3));
userInfoMapper.update(updateWrapper);
}