MyBatisPlus从零到一:快速入门与核心功能详解(2)

米饭8个月前行业资讯457

二、核心功能

刚才的案例中都是以 id 为条件的简单 CRUD,一些复杂条件的 SQL 语句就要用到一些更高级的功能了。


2.1 条件构造器:

除了新增以外,修改、删除、查询的 SQL 语句都需要指定 where 条件。因此 BaseMapper 中提供的相关方法除了以id作为where条件以外,还支持更加复杂的where条件。

image-20241109094626662

参数中的Wrapper就是条件构造的抽象类,其下有很多默认实现,继承关系如图:

image-20241109094741439

Wrapper的子类AbstractWrapper提供了where中包含的所有条件构造方法:(下面的这些方法中,一般来说,第一个参数是 boolean 类型的,是用来做出动态 SQL 中 if 标签同样的效果,如果为 true,表示该条件会添加到 SQL 中,如果为 false,就不会在 SQL 中显示该条件。)

image-20241109094833251

而 QueryWrapper 在 AbstractWrapper 的基础上拓展了一个 select 方法,允许指定查询字段:

image-20241109095236012

而 UpdateWrapper 在 AbstractWrapper 的基础上拓展了一个 set 方法,允许指定 SQL 中的 SET 部分:

image-20241109095317601

2.1.1 QueryWrapper:

无论是修改、删除、查询,都可以使用 QueryWrapper 来构建查询条件。

下面举个例子:

查询:查询出名字中带o的,存款大于等于 1000 元的人的 id,username,info,balance。代码如下:

@Test
void testQueryWrapper() {
    //1. 构造查询条件
    QueryWrapper<User> queryWrapper = new QueryWrapper<User>()
            .select("id","username","info","balance")
            .like("username", "o")
            .ge("balance", 1000);
    //2. 进行查询
    List<User> users = userMapper.selectList(queryWrapper);
    users.forEach(System.out::println);
}


更新:更新用户名为 jack(Mysql 中,是不区分大小写的) 的用户的余额为 2000,代码如下:

@Test
void testUpdateByQueryWrapper(){
    //1. 构造更新条件
    //1.1 构造更新参数
    User user = new User();
    user.setBalance(2000);
    //1.2 构造更新条件
    QueryWrapper<User> queryWrapper = new QueryWrapper<User>()
            .eq("username","jack");
    //2. 进行更新
    userMapper.update(user, queryWrapper);
}

2.1.2 UpdateWrapper:

上面 BaseMapper 中的 update 方法传入 QueryWrapper 的条件更新只能直接进行复制,对于一些复杂的需求就难以实现。

例如:更新id为1,2,4的用户的余额,扣 200,对应的 SQL 应该是:

UPDATE user SET balance = balance - 200 WHERE id in (1, 2, 4)

SET 的赋值结果是基于字段现有值的,这个时候就要利用 UpdateWrapper 中的 setSql 功能了:

@Test
void testUpdateWrapper(){
    //1. 构造更新条件
    UpdateWrapper<User> updateWrapper = new UpdateWrapper<User>()
            .setSql("balance = balance - 200")
            .in("id",List.of(1L,2L,4L));
    //2. 进行更新
    userMapper.update(null, updateWrapper);
}

2.1.3 LambdaQueryWrapper || LambdaUpdateWrapper:

无论是 QueryWrapper 还是 UpdateWrapper 在构造条件的时候都需要写死字段名称,这在编程规范中显然是不推荐的。


那怎么样才能不写字段名,又能知道字段名呢?


一种办法是基于变量的gettter方法结合反射技术。因此我们只要将条件对应的字段的getter方法传递给 MybatisPlus,它就能计算出对应的变量名,从而知道字段名。而传递方法可以使用 JDK8 中的方法引用和Lambda表达式。 因此 MybatisPlus 又提供了一套基于Lambda 的 Wrapper ,包含两个:


LambdaQueryWrapper

LambdaUpdateWrapper

分别对应 QueryWrapper 和 UpdateWrapper。


LambdaQueryWrapper 的使用演示如下:

@Test
void testLambdaQueryWrapper() {
    //1. 构造查询条件
    LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<User>()
            .select(User::getId, User::getUsername, User::getInfo, User::getBalance)
            .like(User::getUsername, "o")
            .ge(User::getBalance, 1000);
    //2. 进行查询
    List<User> users = userMapper.selectList(lambdaQueryWrapper);
    users.forEach(System.out::println);
}

LambdaUpdateWrapper 的使用演示如下:(需要注意的是,如果是使用 setSql 的话,是不能使用 Lambda 表达式的,如果需要达成在原来的基础上进行扣减,且不使用 setSql 的话,可以从 Java 代码的业务逻辑出发,先查出当前 user 的信息,在对该 user 的余额进行扣减,再更新回去即可,实现起来并不难,所以下面演示的代码就简单的进行赋值)。

@Test
void testLambdaUpdateWrapper() {
    // 1. 构造更新条件
    LambdaUpdateWrapper<User> lambdaUpdateWrapper = new LambdaUpdateWrapper<User>()
            .set(User::getBalance, 1000)
            .in(User::getId, List.of(1L, 2L, 3L));
    //2. 进行更新
    userMapper.update(null, lambdaUpdateWrapper);
}

2.2 自定义 SQL:

在演示 UpdateWrapper 的案例中,我们在代码中编写了更新的 SQL 语句:

image-20241109110422232

这种写法在某些企业也是不允许的,因为 SQL 语句最好都维护在持久层,而不是业务层。 就当前案例来说,由于条件是 in 语句,只能将SQL 写在 Mapper.xml 文件,利用 foreach 来生成动态 SQL。 这实在是太麻烦了。假如查询条件更复杂,动态 SQL 的编写也会更加复杂。


所以,MybatisPlus 提供了自定义 SQL 功能,可以让我们利用 Wrapper 生成查询条件,再结合 Mapper.xml 编写 SQL。这样的做法算是综合了一下利弊,将 where 之前的 sql 语句放在 Mapper 层,进行编写,where 后面的语句在业务层进行编写,虽然还是不符合规范,但是较之前全部 sql 都放在业务层,也算是有了提升,MybatisPlus 的优势在于编写 where 条件非常的方便,使用 xml 文件进行动态 sql 编写,太繁琐了。


2.2.1 基本用法:

@Test
void testCustomWrapper(){
    //1. 构造 Wrapper 条件
    QueryWrapper<User> queryWrapper = new QueryWrapper<User>()
            .in("id", List.of(1L, 2L, 4L));
    //2. 进行扣除余额
    userMapper.deductBalanceByIds(queryWrapper,200);
}
// Mapper 层
void deductBalanceByIds(@Param("ew") QueryWrapper<User> queryWrapper, @Param("balance") int balance);
// xml 文件
<update id="deductBalanceByIds">
        update user set balance = balance - #{balance} ${ew.customSqlSegment}
</update>

注意:@Param(“ew”) QueryWrapper queryWrapper,中 @Param(“ew”) 是固定写法,${ew.customSqlSegment} 也是固定写法。


2.2.2 多表关联:

理论上来讲 MyBatisPlus 是不支持多表查询的,不过我们可以利用 Wrapper 中自定义条件,结合自定义 SQL 来实现多表查询的效果。例如,我们要查询出所有收货地址在北京的并且用户 id 在1、2、4之中的用户,要是自己基于 mybatis 实现 SQL,大概是这样的:

<select id="queryUserByIdAndAddr" resultType="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>

可以看出其中 where 的编写还是挺复杂的,如果业务复杂一些,这里的 SQL 会更变态。

但是基于自定义 SQL 结合 Wrapper 的玩法,我们就可以利用 Wrapper 来构建查询条件,然后手写 SELECT 及 FROM 部分,实现多表查询。

对应的代码如下:

// VO 实体类
@Data
public class UserVO extends User {
    private String city;
}
// Test 测试类
@Test
void testCustomJoinWrapper(){
    //1. 构造 Wrapper 条件
    QueryWrapper<UserVO> queryWrapper = new QueryWrapper<UserVO>()
        .in("u.id",List.of(1L,2L,4L))
        .eq("a.city","北京");
    //2. 进行查询
    List<UserVO> users = userMapper.queryUserAndAddressByWrapper(queryWrapper);
}
// Mapper 层代码
List<UserVO> queryUserAndAddressByWrapper(@Param("ew") QueryWrapper<UserVO> queryWrapper); 

// xml 文件中的对应代码
<select id="queryUserAndAddressByWrapper" resultType="com.gobeyye.mp.domain.vo.UserVO">
    select u.*,a.city from user u inner join address a
    on u.id = a.user_id ${ew.customSqlSegment}
</select>

对于上面这段 Mybatis plus 使用自定义 SQL 写多表查询,我还是更加建议使用原来的 Mybatis,配合使用 AI ,也不会太繁琐,且代码的可读性更好,不用一条 SQL 多个文件里面去找。

本文系转载,版权归原作者所有,如若侵权请联系我们进行删除!  

云掣基于多年在运维领域的丰富时间经验,编写了《云运维服务白皮书》,欢迎大家互相交流学习:

《云运维服务白皮书》下载地址:https://fs80.cn/v2kbbq

想了解更多大数据运维托管服务、数据库运维托管服务、应用系统运维托管服务的的客户,欢迎点击云掣官网沟通咨询:https://yunche.pro/?t=shequ


相关文章

Spring AOP 实战指南:从入门到精通(4)

Spring AOP 实战指南:从入门到精通(4)

3.5 切点表达式:上面的代码中,我们一直在使用切点表达式来描述切点。下面我们来介绍一下切点表达式的语法。切点表达式常见有两种表达方式:execution:根据方法的签名来匹配。@annotation...

Linux 环境下Docker将镜像打包导出到本地,上传至内网服务器(八)

Linux 环境下Docker将镜像打包导出到本地,上传至内网服务器(八)

背景docker将镜像导出到本地,上传至内网服务器上背景:在企业中往往出现了内网不能和外网相通,不能使用docker pull命令来拉取镜像,这个时候我们就可以考虑在有所需镜像的服务器上导出...

BetaFlight模块设计之三十二:MSP协议模块分析

BetaFlight模块设计之三十二:MSP协议模块分析

基于BetaFlight开源代码框架简介的框架设计,逐步分析内部模块功能设计。1. MSP协议模块1.1 MSP描述MSP是Multiwii Serial Protocol的缩写,截止目前为止有两个版...

从传统运维到SRE的转型路线

从传统运维到SRE的转型路线

从传统运维向SRE(Site Reliability Engineering)转型需要技术技能、思维方式和工作模式的全面转变。以下是详细的转型路线图:一、理解SRE的核心理念SRE的定义SRE是Goo...

Docker:容器化和虚拟化

Docker:容器化和虚拟化

虚拟化虚拟化是一种资源管理技术,它将计算机的各种实体资源(如CPU、内存、磁盘空间、网络适配器等)予以抽象、转换后呈现出来,并可供分割、组合为一个或多个电脑配置环境。这些资源的新虚拟部分是不受现有资源...

ubuntu下如何查看显卡及显卡驱动

ubuntu下如何查看显卡及显卡驱动

ubuntu下如何查看显卡及显卡驱动使用nvidia-smi 工具查看查看显卡型号nvida-smi -L$ nvidia-smi -L GPU 0:&nbs...

发表评论    

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。