目录
LambdaQueryWrapper和LambdaUpdateWrapper
完整教程资料https://juejin.cn/post/7010012164731699207
学习资料https://htmlpreview.github.io/?https://github.com/caolib/note/blob/master/mybatis-plus.htmlMyBatis-Plus(简称 MP)是一个 MyBatis的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
1.快速开始
1.1 导入依赖
导入mybatis-plus依赖,包含了mybatis,不用额外再导入mybatis依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.1</version>
</dependency>
application.yml配置mysql数据源
# DataSource Config
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost/mp_plus?serverTimezone=GMT%2B8&characterEncoding=UTF-8&allowMultiQueries=true
username: root
password: 123456
1.2 创建Mapper
为了简化单表CRUD,mp已经提供了对于单表的CRUD操作的接口BaseMapper
,直接继承BaseMapper接口即可直接使用
// 在对应的Mapper上面继承基本的类 BaseMapper
@Mapper
public interface UserMapper extends BaseMapper<User> {
// 所有的CRUD操作都已经编写完成了
}
1.3 增加 @MapperScan扫描注解
@MapperScan("com.mpstudy.mp.mapper")
1.4 测试CRUD
测试BaseMapper中对单表CRUD操作 (基本是基于主键实现的CRUD操作)
///selectBatchIds接受的是一个list
categoryDao.selectBatchIds(req.getCategory2())
.stream()
.map(AurrCategory::getName)
.collect(Collectors.toList())
@Test
public void testInsert() {
User user = new User();
//user.setId(5L);
user.setUsername("ikun23");
user.setPassword("123");
user.setPhone("18688990011");
user.setBalance(200);
user.setInfo(UserInfo.of(24,"英语老师","female"));
user.setCreateTime(LocalDateTime.now());
user.setUpdateTime(LocalDateTime.now());
userMapper.insert(user);
}
@Test
public void testSelectById() {
User user = userMapper.selectById(4L);
System.out.println(user);
}
@Test
public void testSelectByIds() {
selectBatchIds接受的是一个list
List<User> users = userMapper.selectBatchIds(List.of(1, 2, 3));
users.forEach(System.out::println);
}
@Test
public void testUpdate() {
User user = new User();
user.setId(5L);
user.setBalance(3);
user.setInfo(UserInfo.of(24,"英语老师","female"));
user.setCreateTime(LocalDateTime.now());
user.setUpdateTime(LocalDateTime.now());
userMapper.updateById(user);
}
@Test
public void testDelete() {
System.out.println(userMapper.deleteById(5L));
}
总结:只要继承了
BaseMapper
,就能直接对单表进行CRUD操作!
2.常见注解
问题:在刚刚的测试中,我们直接调用BaseMapper中的方法就能对表增删改查,在继承
BaseMapper
的时候我们只是指定了一个泛型<User>
,并没有指定是哪张表,那么mybatis-plus怎么知道我们要操作的是user表呢?它又是怎么知道这张表中的所有字段名呢?解答:其实mp遵从约定大于配置的思想,mp从
User
类推导出数据库中表名为user
,然后根据User类中的所有变量名从驼峰命名转成下划线作为数据库的字段名,从而在调用方法时可以自动生成正确的sql语句。如果我们在创建User类和user表的时候遵从驼峰命名和下划线命名,那么我们不需要做额外的配置,如果类名和表名、属性名和字段名直接不是简单的转换,那么我们就不得不使用一些相应的注解来声明表的信息
@TableName
-
描述:表名注解,标识实体类对应的表
-
使用位置:实体类
@Data //lombok注解
@TableName("sys_user")
public class User {
private Long id;
private String name;
private Integer age;
private String email;
}
@TableId
-
描述:主键注解
-
使用位置:实体类主键字段
@TableName("sys_user")
public class User {
@TableId(type = IdType.AUTO) //自增
private Long id;
private String name;
private Integer age;
private String email;
}
属性 | 类型 | 必须指定 | 默认值 | 描述 |
---|---|---|---|---|
value | String | 否 | "" | 主键字段名 |
type | Enum | 否 | IdType.NONE | 指定主键类型 |
IdType
支持的类型:
值 | 描述 |
---|---|
AUTO | 数据库 ID 自增 |
NONE | 无状态,该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT) |
INPUT | insert 前自行 set 主键值 |
ASSIGN_ID | 分配 ID(主键类型为 Number(Long 和 Integer)或 String)(since 3.3.0),使用接口IdentifierGenerator的方法nextId(默认实现类为DefaultIdentifierGenerator雪花算法) |
ASSIGN_UUID | 分配 UUID,主键类型为 String(since 3.3.0),使用接口IdentifierGenerator的方法nextUUID(默认 default 方法) |
ID_WORKER | 分布式全局唯一 ID 长整型类型(please use ASSIGN_ID) |
UUID | 32 位 UUID 字符串(please use ASSIGN_UUID) |
ID_WORKER_STR | 分布式全局唯一 ID 字符串类型(please use ASSIGN_ID) |
常见的id设计:
AUTO
:利用数据库的id自增长
INPUT
:手动生成id
ASSIGN_ID
:雪花算法生成Long
类型的全局唯一id,这是默认的ID策略
@TableField
-
描述:字段注解(非主键),映射到数据库中的字段
@TableName("sys_user")
public class User {
@TableId
private Long id;
@TableField("nickname")
private String name;
@TableField(value = "confirm_time")
private LocalDateTime confirmTime;
@TableField(value = "create_time", updateStrategy = FieldStrategy.NEVER)
private LocalDateTime createTime;
@TableField(value = "update_time", updateStrategy = FieldStrategy.NEVER)
private LocalDateTime updateTime;
}
4.核心功能
4.1 条件构造器
条件构造器 | MyBatis-Plushttps://baomidou.com/guides/wrapper/除了新增以外,修改、删除、查询的SQL语句都需要指定where条件。因此BaseMapper中提供的相关方法除了以id
作为where
条件以外,还支持更加复杂的where
条件。
4.1.1 QueryWrapper
查询:查询名字带有o,且存款大于等于1000的人
select id,username,info,balance
from user
where username like %o% and balance >= 1000;
@Test
public void test02() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
//构建查询条件
wrapper.select("id", "username", "info", "balance")
.like("username", "o")
.ge("balance", 1000);
//查询数据
List<User> users = userMapper.selectList(wrapper);
users.forEach(System.out::println);
}
4.1.2 UpdateWrapper
基于BaseMapper中的update方法更新时只能直接赋值,对于一些复杂的需求就难以实现。 例如:更新id为1,2,4
的用户的余额,扣200,对于的SQL应该是:
UPDATE user SET balance = balance - 200 WHERE id in (1, 2, 4)
SET的赋值结果是基于字段现有值的,这个时候就要利用UpdateWrapper中的setSql功能了:
@Test
void testUpdateWrapper() {
List<Long> ids = List.of(1L, 2L, 4L);
// 1.生成SQL
UpdateWrapper<User> wrapper = new UpdateWrapper<User>()
.setSql("balance = balance - 200") // SET balance = balance - 200
.in("id", ids); // WHERE id in (1, 2, 4)
// 2.更新,注意第一个参数可以给null,也就是不填更新字段和数据,
// 而是基于UpdateWrapper中的setSQL来更新
userMapper.update(null, wrapper);
}
无论是QueryWrapper还是UpdateWrapper在构造条件的时候都需要写死字段名称,可能会出现字符串写错的现象,因此MybatisPlus又提供了一套基于Lambda的Wrapper,包含两个:
LambdaQueryWrapper和LambdaUpdateWrapper
-
LambdaQueryWrapper:这是一个基于 Lambda 表达式的查询条件构造器,它通过 Lambda 表达式来引用实体类的属性,从而避免了硬编码字段名。这种方式提高了代码的可读性和可维护性,尤其是在字段名可能发生变化的情况下。
-
LambdaUpdateWrapper:类似于 LambdaQueryWrapper,LambdaUpdateWrapper 是基于 Lambda 表达式的更新条件构造器。它允许你使用 Lambda 表达式来指定更新字段和条件,同样避免了硬编码字段名的问题。
select id,username,info,balance
from user
where username like %o% and balance >= 1000;
@Test
public void test02() {
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
//构建查询条件
//由原来的字符串变成getter函数对象获取类属性
wrapper.select(User::getId, User::getUsername, User::getInfo, User::getBalance)
.like(User::getUsername, "o")
.ge(User::getBalance, 1000);
userMapper.selectList(wrapper);
}
条件查询:
lt: 小于(<) less, gt:大于(>) great, eq:等于(=)
or()就相当于我们sql语句中的or
关键字,不加默认是and
lqw.lt(User::getAge, 10).or().gt(User::getAge, 30);
List<User> userList = userDao.selectList(lqw);
---->
SELECT id,name,password,age,tel FROM user WHERE (age < ? OR age > ?)
in:设置单个字段的 IN 条件,即字段的值在给定的集合中
NULL值处理
if语句控制条件追加
方法1、
Integer minAge=10; //将来有用户传递进来,此处简化成直接定义变量了
Integer maxAge=null; //将来有用户传递进来,此处简化成直接定义变量了
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
if(minAge!=null){
lqw.gt(User::getAge, minAge);
}
if(maxAge!=null){
lqw.lt(User::getAge, maxAge);
}
List<User> userList = userDao.selectList(lqw);
userList.forEach(System.out::println);
方法2、
Integer minAge=10; //将来有用户传递进来,此处简化成直接定义变量了
Integer maxAge=null; //将来有用户传递进来,此处简化成直接定义变量了
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
//参数1:如果表达式为true,那么查询才使用该条件
lqw.gt(minAge !=null , User::getAge, minAge)
.lt(maxAge !=null , User::getAge, maxAge);
List<User> userList = userDao.selectList(lqw);
userList.forEach(System.out::println);
查询投影
(不查询所有字段,只查询出指定内容的数据)
最终的sql语句为:SELECT id,name,age,tel FROM user
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
lqw.select(User::getId,User::getName,User::getAge);
或则
QueryWrapper<User> lqw = new QueryWrapper<User>();
lqw.select("id","name","age","tel");
- 聚合与分组查询,无法使用lambda表达式来完成
逻辑删除
逻辑删除支持 | MyBatis-Plushttps://baomidou.com/guides/logic-delete/
4.2 自定义SQL
4.2.1 基本使用
MybatisPlus提供了自定义SQL功能,可以让我们利用Wrapper生成查询条件,再结合Mapper.xml或注解编写SQL
UPDATE user SET balance = balance - 200 WHERE id in (1, 2, 4)
update user set balance = balance - 200
使用注解完成//将wrapper作为ew,并使用ew.customSqlSegment取出条件是固定写法
@Update("update tb_user set balance = balance - #{amount} ${ew.customSqlSegment}") void updateBalanceByWrapper(@Param("amount") int amount, @Param("ew") LambdaQueryWrapper<User> wrapper);
where id in(1,2,4)
使用自定义sql完成,将wrapper作为参数传入自定义的方法中通过
${ew.customSqlSegment}
转化成where id in (1,2,4) !!!!!!
@Test public void testCustomSql() { int amount = 200; LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>(); wrapper.in(User::getId, 1, 2, 4); userMapper.updateBalanceByWrapper(amount, wrapper); }
4.2.2 多表联查
理论上来将MyBatisPlus是不支持多表查询的,不过我们可以利用Wrapper中自定义条件结合自定义SQL来实现多表查询的效果。
例如,我们要查询出所有收货地址在北京的并且用户id在1、2、4之中的用户
普通实现
@Test
public interface UserMapper extends BaseMapper<User> {
List<User> queryUserByIdAndAddr(List<Long> ids, String city);
}
<select id="queryUserByIdAndAddr" resultType="com.itheima.mp.domain.po.User">
SELECT *
FROM user u
INNER JOIN address a ON u.id = a.user_id
WHERE u.id
<foreach collection="ids" separator="," item="id" open="IN (" close=")">
#{id}
</foreach>
AND a.city = #{city}
</select>
Wrapper构建查询条件实现
基于自定义SQL结合Wrapper的玩法,我们就可以利用Wrapper来构建查询条件,然后手写SELECT及FROM部分,实现多表查询。 查询条件这样来构建:
@Test
void testCustomJoinWrapper() {
// 1.准备自定义查询条件
QueryWrapper<User> wrapper = new QueryWrapper<User>()
.in("u.id", List.of(1L, 2L, 4L))
.eq("a.city", "北京");
// 2.调用mapper的自定义方法
List<User> users = userMapper.queryUserByWrapper(wrapper);
users.forEach(System.out::println);
}
1、在UserMapper中自定义方法:
@Select("SELECT u.* FROM user u INNER JOIN address a ON u.id = a.user_id ${ew.customSqlSegment}")
List<User> queryUserByWrapper(@Param("ew")QueryWrapper<User> wrapper);
2、也可以在UserMapper.xml
中写SQL:
<select id="queryUserByIdAndAddr" resultType="com.itheima.mp.domain.po.User">
SELECT * FROM user u INNER JOIN address a ON u.id = a.user_id ${ew.customSqlSegment}
</select>
总结:where条件可以使用wrapper构建,然后作为参数传递