文章目录
- 1. 动态SQL
- 1.1 <if>标签
- 1.2 <trim>标签
- 1.3 <where>标签
- 1.4 <set>标签
- 1.5 <foreach>标签
- 1.6 <include>标签
- 2. MyBatis-Plus
- 3. MyBatis-Plus复杂操作
- 3.1 常见注解
- 3.1.1 @TableName
- 3.1.2 @TableField
- 3.1.3 @TableId
- 3.2 打印日志
- 3.3 条件构造器
- 3.3.1 QueryWrapper
- 3.3.2 UpdateWrapper
- 3.3.3 LambdaQueryWrapper
- 3.3.4 LambdaUpdateWrapper
1. 动态SQL
动态 SQL 是Mybatis的强大特性之一,能够完成不同条件下不同的 sql 拼接
1.1 <if>标签
可以用if标签来实现非必填字段
比如性别 gender 为非必填字段
接口定义
IntegerinsertUserByCondition(Integergender);Xml实现
<insertid="insertUserByCondition">INSERT into userinfo ( username, password, age,<iftest="gender != null">gender,</if>phone) VALUES ( #{username}, #{password}, #{age},<iftest="gender != null">#{gender},</if>#{phone})</insert>或者使用注解方式, 但并不推荐, 感兴趣可自行了解
test标签中的gender字段, 是传入对象的属性, 要跟Java对象的属性对应, 而不是数据库的字段
不可以不判断直接把字段设置为null, 这种情况下, 如果gender字段有默认值, 就会设置为默认值
1.2 <trim>标签
之前的插入用户功能,只是有一个 gender 字段可能是选填项,如果有多个字段,一般考虑使用标签结合标签,对多个字段都采取动态生成的方式.
- prefix:表示整个语句块,以prefix的值作为前缀
- suffix:表示整个语句块,以suffix的值作为后缀
- prefixOverrides:表示整个语句块要去除掉的前缀
- suffixOverrides:表示整个语句块要去除掉的后缀
<insertid="insertUserByCondition">INSERT INTO userinfo<trimprefix="("suffix=")"suffixOverrides=","><iftest="username !=null">username,</if><iftest="password !=null">`password`,</if><iftest="age != null">age,</if><iftest="gender != null">gender,</if><iftest="phone != null">phone,</if></trim>VALUES<trimprefix="("suffix=")"suffixOverrides=","><iftest="username !=null">#{username},</if><iftest="password !=null">#{password},</if><iftest="age != null">#{age},</if><iftest="gender != null">#{gender},</if><iftest="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,其他的保留
List<UserInfo>queryByCondition(UserInfouserInfo);<selectid="queryByCondition"resultType="com.example.test.demos.dao.UserInfo">select id, username, age, gender, phone, delete_flag, create_time, update_time from userinfo<where><iftest="age != null">and age = #{age}</if><iftest="gender != null">and gender = #{gender}</if><iftest="deleteFlag != null">and delete_flag = #{deleteFlag}</if></where></select>注意语句中数据库字段和Java对象字段的映射关系
1.4 <set>标签
需求: 根据传入的用户对象属性来更新用户数据,可以使用标签来指定动态内容
接口定义: 根据传入的用户 id 属性,修改其他不为 null 的属性
IntegerupdateUserByCondition(UserInfouserInfo);<updateid="updateUserByCondition"><set><iftest="username != null">username = #{username}</if><iftest="age != null">age = #{age},</if><iftest="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, 删除用户数据
voiddeleteByIds(@Param("ids")List<Integer>ids);XML实现
<deleteid="deleteByIds">delete from userinfo where id in<foreachcollection="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片段
<sqlid="allColumn">id, username, age, gender, phone, delete_flag, create_time, update_time</sql>通过 <include> 标签在原来抽取的地方进行引用
<selectid="queryAllUser"resultMap="BaseMap">select<includerefid="allColumn"></include>from userinfo</select>2. MyBatis-Plus
MyBatis-Plus(简称 MP) 是一个 MyBatis 的增强工具, 在 MyBatis 的基础上只做增强不做改变, 为简化开发. 提高效率而生。
MybatisPlus提供了一个基础的 BaseMapper 接口,已经实现了单表的CRUD, 我们自定义的Mapper只需要继承这个BaseMapper, 就无需自己实现单表CRUD
@MapperpublicinterfaceUserInfoMapperextendsBaseMapper<UserInfo>{}UserInfoMapper 在继承 BaseMapper 时, 指定了一个泛型, 这个UserInfo就是与数据库表相对应的实体类
默认情况下:
- 表名: 实体类的驼峰表示法转换成蛇形表示法(下划线分割), 作为表名. 比如UserInfo -> user_info
- 字段: 根据实体类的属性名, 转换为蛇形表示法作为字段名. 比如deleteFlag -> delete_flag
- 主键: 默认为id
3. MyBatis-Plus复杂操作
3.1 常见注解
3.1.1 @TableName
我们可以通过 @TableName 来标识实体类对应的表
@Data@TableName("user_info")publicclassUserInfo{privateIntegerid;privateStringusername;privateStringpassword;privateIntegerage;privateIntegergender;privateStringphone;privateIntegerdeleteFlag;privateDatecreateTime;privateDateupdateTime;}3.1.2 @TableField
我们可以通过 @TableField 来标识 对应的字段名
@Data@TableName("user_info")publicclassUserInfo{privateIntegerid;privateStringusername;privateStringpassword;privateIntegerage;privateIntegergender;privateStringphone;@TableField("delete_flag")privateIntegerdeleteFlag;privateDatecreateTime;privateDateupdateTime;}3.1.3 @TableId
我们可以通过 @TableId 来 指定对应的主键
@Data@TableName("user_info")publicclassUserInfo{@TableId("id")privateIntegerid;privateStringusername;privateStringpassword;privateIntegerage;privateIntegergender;privateStringphone;@TableField("delete_flag")privateIntegerdeleteFlag;privateDatecreateTime;privateDateupdateTime;}如果属性名和字段名不一致, 需要在 @TableId 指明对应的字段名;
属性名和字段一致的情况下, 直接加 @TableId 注解就可以
3.2 打印日志
Mybatis-Plus配置日志如下:
mybatis-plus:configuration:# 配置打印 MyBatis日志log-impl:org.apache.ibatis.logging.stdout.StdOutImpl3.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来构建查询条件这里只简单介绍查询和更新
SELECTid,username,password,ageFROMuser_infoWHEREage=18ANDusername"%min%"voidtestQueryWrapper(){QueryWrapper<UserInfo>userInfoQueryWrapper=newQueryWrapper<UserInfo>().select("id","username","password","age").eq("age",18).like("username","min");List<UserInfo>userInfos=userInfoMapper.selectList(userInfoQueryWrapper);}QueryWrapper并不只用于查询语句, 无论是修改, 删除, 查询, 都可以使用QueryWrapper来构建查询条件这里只简单介绍查询和更新
UPDATEuser_infoSETdelete_flag=?WHEREage<20voidtestUpdateByQueryWrapper(){QueryWrapper<UserInfo>userInfoQueryWrapper=newQueryWrapper<UserInfo>().lt("age",20);UserInfouserInfo=newUserInfo();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, 在不创建实体对象的情况下, 直接设置更新字段和条件
UPDATEuser_infoSETdelete_flag=0,age=5WHEREidIN(1,2,3)voidtestUpdateByUpdateWrapper(){UpdateWrapper<UserInfo>updateWrapper=newUpdateWrapper<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
voidtestLambdaQueryWrapper(){QueryWrapper<UserInfo>queryWrapper=newQueryWrapper<UserInfo>();queryWrapper.lambda().select(UserInfo::getUsername,UserInfo::getPassword,UserInfo::getAge).eq(UserInfo::getId,1);userInfoMapper.selectList(queryWrapper);}3.3.4 LambdaUpdateWrapper
voidtestLambdaUpdateWrapper(){UpdateWrapper<UserInfo>updateWrapper=newUpdateWrapper<UserInfo>();updateWrapper.lambda().set(UserInfo::getDeleteFlag,0).set(UserInfo::getAge,5).in(UserInfo::getId,List.of(1,2,3));userInfoMapper.update(updateWrapper);}