【JAVA】黑马MybatisPlus 学习笔记【二】【核心功能】

news2024/12/23 15:49:12

2.核心功能

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

2.1.条件构造器

除了新增以外,修改、删除、查询的SQL语句都需要指定where条件。因此BaseMapper中提供的相关方法除了以id作为where条件以外,还支持更加复杂的where条件。
在这里插入图片描述
参数中的Wrapper就是条件构造的抽象类,其下有很多默认实现,继承关系如图:
在这里插入图片描述
Wrapper的子类AbstractWrapper提供了where中包含的所有条件构造方法:

在这里插入图片描述
QueryWrapperAbstractWrapper的基础上拓展了一个select方法,允许指定查询字段:
在这里插入图片描述
UpdateWrapperAbstractWrapper的基础上拓展了一个set方法,允许指定SQL中的SET部分:
在这里插入图片描述

2.1.1.QueryWrapper

无论是修改、删除、查询,都可以使用QueryWrapper来构建查询条件。接下来看一些例子:
在这里插入图片描述

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

@Test
void testQueryWrapper() {
    // 1.构建查询条件 where name like "%o%" AND balance >= 1000
    QueryWrapper<User> wrapper = new QueryWrapper<User>()
            .select("id", "username", "info", "balance")
            .like("username", "o")
            .ge("balance", 1000);
    // 2.查询数据
    List<User> users = userMapper.selectList(wrapper);
    users.forEach(System.out::println);
}

更新:更新用户名为Jack的用户的余额为2000,代码如下:

@Test
void testUpdateByQueryWrapper() {
    // 1.构建查询条件 where name = "Jack"
    QueryWrapper<User> wrapper = new QueryWrapper<User>().eq("username", "Jack");
    // 2.更新数据,user中非null字段都会作为set语句
    User user = new User();
    user.setBalance(2000);
    userMapper.update(user, wrapper);
}
2.1.2.UpdateWrapper

在这里插入图片描述

基于BaseMapper中的update方法更新时只能直接赋值,对于一些复杂的需求就难以实现。
例如:更新id1,2,4的用户的余额,扣200,对应的SQL应该是:

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

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

  // 更新id为1,2,4的用户的余额,扣200
    @Test
    void testUpdateWrapper() {
        // List<Long> ids = new ArrayList<>(Arrays.asList(1L, 2L, 4L)); //JDK8 可以使用这种方式
        List<Long> ids = List.of(1L, 2L, 4L); // JDK9 之后才有的of方法
        // 1.生成SQL
        UpdateWrapper<User> userUpdateWrapper = new UpdateWrapper<User>()
                .setSql("balance = balance -200")
                .in("id", ids); // WHERE id in (1, 2, 4)

        // 2.更新,注意第一个参数可以给null,也就是不填更新字段和数据,
        // 而是基于UpdateWrapper中的setSql来更新
        userMapper.update(null, userUpdateWrapper);
    }
2.1.3.LambdaQueryWrapper

无论是QueryWrapper还是UpdateWrapper在构造条件的时候都需要写死字段名称,会出现字符串魔法值。这在编程规范中显然是不推荐的。
那怎么样才能不写字段名,又能知道字段名呢?

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

  • LambdaUpdateWrapper
    分别对应QueryWrapperUpdateWrapper

其使用方式如下:

  @Test
    void testLambdaQueryWrapper() {
        // 1.构建查询条件 where name like "%o%" AND balance >= 1000
        LambdaQueryWrapper<User> userLambdaQueryWrapper = 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(userLambdaQueryWrapper);
        users.forEach(System.out::println);
    }

    // 更新用户名为jack的用户的余额为2000
    @Test
    void testLambdaUpdateByQueryWrapper() {
        // 1.构建查询条件 where name = "Jack"
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<User>()
                .eq(User::getUsername, "Jack");
        // 2.更新数据,user中非null字段都会作为set语句
        User user = new User();
        user.setBalance(2000);
        userMapper.update(user, wrapper);
    }

2.2.自定义SQL

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

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

所以,MybatisPlus提供了自定义SQL功能,可以让我们利用Wrapper生成查询条件,再结合Mapper.xml编写SQL

2.2.1.基本用法

以当前案例来说,我们可以这样写:

@Test
void testCustomWrapper() {
    // 1.准备自定义查询条件
    List<Long> ids = List.of(1L, 2L, 4L);
    QueryWrapper<User> wrapper = new QueryWrapper<User>().in("id", ids);

    // 2.调用mapper的自定义方法,直接传递Wrapper
    userMapper.deductBalanceByIds(200, wrapper);
}

然后在UserMapper中自定义SQL

package com.itheima.mp.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.itheima.mp.domain.po.User;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Update;
import org.apache.ibatis.annotations.Param;

public interface UserMapper extends BaseMapper<User> {
    @Select("UPDATE user SET balance = balance - #{money} ${ew.customSqlSegment}")
    void deductBalanceByIds(@Param("money") int money, @Param("ew") QueryWrapper<User> wrapper);
}

这样就省去了编写复杂查询条件的烦恼了。

总结

mapper方法参数中用Param注解声明wrapper变量名称,必须是ew
为什么必须要用ewmybatisPlus 源码规定的,这也成为一种约定俗成的规范。当然,你也可以手动修改生成的代码,将 ew 改为其他名字,但这样可能导致一些示例代码不适用。

在这里插入图片描述

2.2.2.多表关联

理论上来讲MyBatisPlus是不支持多表查询的,不过我们可以利用Wrapper中自定义条件结合自定义SQL来实现多表查询的效果。
例如,我们要查询出所有收货地址在北京的并且用户id1、2、4之中的用户
要是自己基于mybatis实现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
      WHERE u.id
      <foreach collection="ids" separator="," item="id" open="IN (" close=")">
          #{id}
      </foreach>
      AND a.city = #{city}
  </select>

可以看出其中最复杂的就是WHERE条件的编写,如果业务复杂一些,这里的SQL会更变态。
但是基于自定义SQL结合Wrapper的玩法,我们就可以利用Wrapper来构建查询条件,然后手写SELECTFROM部分,实现多表查询。

查询条件这样来构建:

@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);
}

然后在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);

当然,也可以在UserMapper.xml中写SQL

<select id="queryUserByWrapper" resultType="com.itheima.mp.domain.po.User">
    SELECT * FROM user u INNER JOIN address a ON u.id = a.user_id ${ew.customSqlSegment}
</select>

2.3.Service接口

MybatisPlus不仅提供了BaseMapper,还提供了通用的Service接口及默认实现,封装了一些常用的service模板方法。
通用接口为IService,默认实现为ServiceImpl,其中封装的方法可以分为以下几类:

  • save:新增
  • remove:删除
  • update:更新
  • get:查询单个结果
  • list:查询集合结果
  • count:计数
  • page:分页查询
2.3.1.CRUD

我们先俩看下基本的CRUD接口。

新增:

在这里插入图片描述

  • save是新增单个元素
  • saveBatch是批量新增
  • saveOrUpdate是根据id判断,如果数据存在就更新,不存在则新增
  • saveOrUpdateBatch是批量的新增或修改

删除:

在这里插入图片描述

  • removeById:根据id删除
  • removeByIds:根据id批量删除
  • removeByMap:根据Map中的键值对为条件删除
  • remove(Wrapper<T>):根据Wrapper条件删除
  • removeBatchByIds:暂不支持

修改:
在这里插入图片描述

  • updateById:根据id修改
  • update(Wrapper<T>):根据UpdateWrapper修改,Wrapper中包含setwhere部分
  • update(T,Wrapper<T>):按照T内的数据修改与Wrapper匹配到的数据
  • updateBatchById:根据id批量修改

查询

Get

在这里插入图片描述

  • getById:根据id查询1条数据
  • getOne(Wrapper<T>):根据Wrapper查询1条数据
  • getBaseMapper:获取Service内的BaseMapper实现,某些时候需要直接调用Mapper内的自定义SQL时可以用这个方法获取到Mapper

List

在这里插入图片描述

  • listByIds:根据id批量查询
  • list(Wrapper<T>):根据Wrapper条件查询多条数据
  • list():查询所有

Count

在这里插入图片描述

  • count():统计所有数量
  • count(Wrapper<T>):统计符合Wrapper条件的数据数量

getBaseMapper

当我们在service中要调用Mapper自定义SQL时,就必须获取service对应的Mapper,就可以通过这个方法:
在这里插入图片描述

项目结构如下:

在这里插入图片描述
来到 UserService 中创建 测试类
在这里插入图片描述

在这里插入图片描述

接下来,我们快速实现下面4个接口:
在这里插入图片描述

首先,我们在项目中引入几个依赖:
pom.xml

<!--swagger-->
<dependency>
    <groupId>com.github.xiaoymin</groupId>
    <artifactId>knife4j-openapi2-spring-boot-starter</artifactId>
    <version>4.1.0</version>
</dependency>
<!--web-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

然后在application.xml需要配置swagger信息:

knife4j:
  enable: true
  openapi:
    title: 用户管理接口文档
    description: "用户管理接口文档"
    email: zhanghuyi@itcast.cn
    concat: 墨苒孤
    url: https://www.itcast.cn
    version: v1.0.0
    group:
      default:
        group-name: default
        api-rule: package
        api-rule-resources:
          - com.itheima.mp.controller

Alt + 8 选择 springboot 服务,启动,浏览器输入 http://localhost:8080/doc.html#/home
在这里插入图片描述
然后,接口需要两个实体:

  • UserFormDTO:代表新增时的用户表单
  • UserVO:代表查询的返回结果
    首先是UserFormDTO
package com.itheima.mp.domain.dto;

import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

@Data
@ApiModel(description = "用户表单实体")
public class UserFormDTO {

    @ApiModelProperty("id")
    private Long id;

    @ApiModelProperty("用户名")
    private String username;

    @ApiModelProperty("密码")
    private String password;

    @ApiModelProperty("注册手机号")
    private String phone;

    @ApiModelProperty("详细信息,JSON风格")
    private String info;

    @ApiModelProperty("账户余额")
    private Integer balance;
}

然后是UserVO

package com.itheima.mp.domain.vo;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

@Data
@ApiModel(description = "用户VO实体")
public class UserVO {
    
    @ApiModelProperty("用户id")
    private Long id;
    
    @ApiModelProperty("用户名")
    private String username;
    
    @ApiModelProperty("详细信息")
    private String info;

    @ApiModelProperty("使用状态(1正常 2冻结)")
    private Integer status;
    
    @ApiModelProperty("账户余额")
    private Integer balance;
}

@Autowired 注入,Spring 并不推荐,而是推荐我们使用构造函数注入
在这里插入图片描述
构造函数注入如下:
在这里插入图片描述
但是这样会带来一个问题,如果成员很多,构造函数就会很多,看起来很繁琐。那么就可以使用Lombok 注解帮我们简化
在这里插入图片描述
上图这样看起来是可以了,但是,一个类可以有很多的成员,并不是每一个都是需要注入的。那么怎么区分哪些需要注入,哪些不需要注入呢?
可以给 成员 加上 final 来区分, 这样就必须在类初始化的时候,对加了 final的成员进行初始化,所以最终写法如下:
在这里插入图片描述
@RequiredArgsConstructor表示 会对 final 修饰的成员进行创建构造函数,没有修饰就不会生成构造函数

新增接口实现:

@PostMapping
    @ApiOperation("新增用户")
    public void saveUser(@RequestBody UserFormDTO userFormDTO) {
        // 1.转换DTO为PO
        User user = BeanUtil.copyProperties(userFormDTO, User.class);
        // 2.新增
        userService.save(user);
    }

由于传入的 是 DTO, 而我们需要保存的是 PO,所以需要先把DTO转换为PO。这里使用胡图工具包,其余接口同理

 @DeleteMapping("/{id}")
    @ApiOperation("删除用户")
    public void removeUserById(@PathVariable("id") Long userId){
        userService.removeById(userId);
    }

    @GetMapping("/{id}")
    @ApiOperation("根据id查询用户")
    public UserVO queryUserById(@PathVariable("id") Long userId){
        // 1.查询用户
        User user = userService.getById(userId);
        // 2.处理po转vo
        return BeanUtil.copyProperties(user, UserVO.class);
    }

    @GetMapping
    @ApiOperation("根据id集合查询用户")
    public List<UserVO> queryUserByIds(@RequestParam("ids") List<Long> ids){
        // 1.查询用户
        List<User> users = userService.listByIds(ids);
        // 2.处理po转vo (集合用copyToList)
        return BeanUtil.copyToList(users, UserVO.class);
    }

代码在github https://github.com/RanGuMo/mp-demo

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1308973.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

智能优化算法应用:基于旗鱼算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于旗鱼算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于旗鱼算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.旗鱼算法4.实验参数设定5.算法结果6.参考文献7.MA…

【教程】查看GPU与PCIe版本和匹配速率

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhang.cn] PCIe理论速度对照表 bash脚本 #!/bin/bash# 查找所有 NVIDIA GPU 设备的设备ID及其类型 device_info$(lspci | grep -i nvidia | egrep "VGA compatible controller|3D controller" | awk {print $1, …

JavaEE:单例模式(饿汉模式和懒汉模式)精讲

前言 什么是单例模式&#xff1f; 其实用通俗的话就是程序猿约定俗成的一些东西&#xff0c;就比如如果你继承了一个抽象类&#xff0c;你就要重写里面的抽象方法&#xff0c;如果你实现了一个接口&#xff0c;你就要重写里面的方法。如果不进行重写&#xff0c;那么编译器就会…

ajax和Axios快速入门

什么是ajax 概念&#xff1a; Asynchronous JavaScript And XML&#xff0c;异步的JavaScrip和XML&#xff0c;重点在异步。 作用&#xff1a; 1&#xff0c;数据交互&#xff0c;可以通过ajax给服务器发送请求&#xff0c;并获取服务器响应的数据。 2&#xff0c;异步交互&am…

一些程序源码及教程的网站合集~

很多时候我们需要一个快速上手的code demo及教程&#xff0c;除了最常用的【github】&#xff0c;一些中文网站可能会帮助我们更好上手~ 这里提供几个中文网站参考&#xff1a; 【51CTO】&#xff1a; Python 动态手势识别系统hmm 手势识别opencv_mob64ca140d96d9的技术博客…

Vue3-09-条件渲染-v-show 的基本使用

v-show 的作用 v-show 可以根据条件表达式的值【展示】或【隐藏】html 元素。v-show 的特点 v-show 的实现方式是 控制 dom 元素的 css的 display的属性&#xff0c; 因此&#xff0c;无论该元素是否展示&#xff0c;该元素都会正常渲染在页面上&#xff0c; 当v-show 的 条件…

嵌入式系统未来的发展趋势走向???

人工智能和机器学习应用 模型优化&#xff1a; 为了在资源有限的嵌入式系统上运行&#xff0c;将会看到更多的努力投入到精简、优化和量化模型&#xff0c;以适应边缘计算的环境。 边缘推理&#xff1a; 嵌入式设备将更多地执行本地推理&#xff0c;而不是将所有数据发送到云端…

vue的computed中的getter和setter

vue的computed中的getter和setter 定义getter写法setter写法 定义 computed 中可以分成 getter&#xff08;读取&#xff09; 和 setter&#xff08;设值&#xff09;&#xff0c;一般情况下是没有 setter 的&#xff0c;computed 预设只有 getter&#xff0c;也就是只能读取&a…

9.1 字符设备驱动开发

一、字符设备驱动简介 字符设备是 Linux 驱动中最基本的一类设备驱动&#xff0c;字符设备就是一个一个字节&#xff0c;按照字节流进行读写操作的设备&#xff0c;读写数据是分先后顺序的。比如我们最常见的点灯、按键、 IIC、 SPI&#xff0c;LCD 等等都是字符设备&#xff0…

c语言二维数组之字符数组

介绍&#xff1a; 本篇介绍 c语言二维数组之字符数组 其实他和前两种数组 c语言二维数组之整型数组 c语言二维数组之浮点型数组 差不多 就有两处不同 所以 本篇文章重点讲那两点不同 c语言二维数组之字符数组&#xff1a; 第一点不同&#xff1a; 第一个不同点就是 它存储的…

小航助学2023年9月电子学会Scratch四级真题(含题库答题软件账号)

需要在线模拟训练的题库账号请点击 小航助学编程在线模拟试卷系统&#xff08;含题库答题软件账号&#xff09; 单选题3.00分 删除编辑附件图文 答案:A 第1题角色为一个紫色圆圈&#xff0c;运行程序后&#xff0c;舞台上的图案是&#xff1f;&#xff08; &#xff09; A…

imazing是什么软件

imazing是什么软件 iMazing 是世界上最值得信赖的软件,可以将您的信息、音乐、文件和数据从 iPhone 或 iPad 传输到您的 Mac 或 PC。 获得备份、数据提取、媒体和文件传输的强大能力,以及更多 iMazing 功能。 iMazing是一款第三方的苹果iOS设备管理软件。 iMazing- 2 Mac-安装…

CPython(将Python编译为so)

环境 先配一下环境&#xff0c;我使用的是python3.8.5 pip install Cython 编译过程 我们准备一个要编译的文件 test.py def xor(input_string): output_string "" for char in input_string: output_string chr(ord(char) ^ 0x66) return output_string …

对于双显卡电脑,如何分辨现在用的是独立显卡还是集成显卡?

一、问题描述 台式电脑本身自带了集成显卡&#xff0c;然后又购买了一块NVIDIA的独立显卡。 现在&#xff0c;就有疑问了&#xff0c;如何判断你的显示器连接的是独立显卡还是集成显卡呢&#xff1f; 二、NVIDIA双显卡机型 1、在桌面右下角&#xff0c;选择NVIDIA图标&…

【字符串】ABC324E

退役啦&#xff0c;接下来的博客全是图一乐啦 E - Joint Two Strings 题意 思路 统计两个指针的方案数一定是枚举一个&#xff0c;统计另一个 然后因为拼起来之后要包含 t 这个字符串&#xff0c;隐隐约约会感觉到和前缀后缀子序列有关 考虑预处理每个 s[i] 的最长公共前…

C语言-WIN32API介绍

Windows API 从第一个32位的Windows开始就出现了&#xff0c;就叫做Win32API.它是一个纯C的函数库&#xff0c;就和C标准库一样&#xff0c;使你可以写Windows应用程序过去很多Windows程序是用这个方式做出来的 main()? main()成为C语言的入口函数其实和C语言本身无关&…

boost1.55 安装使用教程 windows

第一步 &#xff1a;首先在boost官网上下载库压缩包 添加链接描述 选择自己需要的版本进行下载 解压后执行booststrap.bat 用来生成创建b2.exe 和bjam.exe 拓展&#xff1a;.\b2 --help 了解一下有哪些参数可以配置 默认b2.exe编译后&#xff0c;链接到项目如果出现如下错误…

Docker Compose入门:打造多容器应用的完美舞台

Docker Compose 是一个强大的工具&#xff0c;它允许开发者通过简单的 YAML 文件定义和管理多容器的应用。本文将深入讨论 Docker Compose 的基本概念、常用命令以及高级应用场景&#xff0c;并通过更为丰富和实际的示例代码&#xff0c;助您轻松掌握如何通过 Docker Compose 打…

docker-compose Nginx Proxy Manager

Nginx Proxy Manager前言 Nginx Proxy Manager(以下简称 NPM)就是一个 Nginx 的代理管理器,它最大的特点是简单方便。 美观且安全的管理界面,基于表格轻松创建转发域、重定向、流和 404 主机,而无需了解 Nginx使用 Lets Encrypt 免费 SSL 或提供您自己的自定义 SSL 证书主…

详细说说vuex

Vuex 是什么 Vuex有几个属性及作用注意事项vuex 使用举例Vuex3和Vuex4有哪些区别 创建 Store 的方式在组件中使用 Store辅助函数的用法响应式的改进Vuex4 支持多例模式 Vuex 是什么 Vuex是一个专门为Vue.js应用设计的状态管理构架&#xff0c;它统一管理和维护各个Vue组件的可…