MybatisPlus(MP)基础知识

news2024/9/20 14:56:16

MP

BaseMapper

为了简化单表CRUD,MybatisPlus提供了一个基础的BaseMapper接口,其中已经实现了单表的CRUD,因此我们自定义的Mapper只要实现了这个BaseMapper,就无需自己实现单表CRUD了。

package com.itheima.mp.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.itheima.mp.domain.po.User;

public interface UserMapper extends BaseMapper<User> {
}

@SpringBootTest
class UserMapperTest {

    @Autowired
    private UserMapper userMapper;

    @Test
    void testInsert() {
        User user = new User();
        user.setId(5L);
        user.setUsername("Lucy");
        user.setPassword("123");
        user.setCreateTime(LocalDateTime.now());
        user.setUpdateTime(LocalDateTime.now());
        userMapper.insert(user);
    }

    @Test
    void testSelectById() {
        User user = userMapper.selectById(5L);
        System.out.println("user = " + user);
    }
}

常用注解

UserMapper在继承BaseMapper的时候指定了一个泛型:泛型中的User就是与数据库对应的PO.
MP基于反射拿到实体类对应的属性。
MybatisPlus就是根据PO实体的信息来推断出表的信息,从而生成SQL的。默认情况下:

  • MybatisPlus会把PO实体的类名驼峰转下划线作为表名
  • MybatisPlus会把PO实体的所有变量名驼峰转下划线作为表的字段名,并根据变量类型推断字段类型
  • MybatisPlus会把名为id的字段作为主键
    但很多情况下,默认的实现与实际场景不符,因此MybatisPlus提供了一些注解便于我们声明表信息。

@TableName指定表名
@TableId指定主键
@TableField普通字段
在这里插入图片描述

需要用@TableField的情况:
is开头的字段名会自动去掉is
和关键字重名
不是数据库字段

常见配置

在这里插入图片描述
classpath*:/mapper/**/*.xml,也就是说我们只要把mapper.xml文件放置这个目录下就一定会被加载。

核心功能

条件构造器

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

(1)QueryWrapper

查询:查询出名字中带o的,存款大于等于1000元的人

@Test
void testQueryWrapper() {
    // 1.构建查询条件 where name like "%o%" AND balance >= 1000
    QueryWrapper<User> wrapper = new QueryWrapper<User>()//指定泛型User是为了确保在构建查询条件时,针对的是 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.要更新的数据
	User user = new User();
    user.setBalance(2000);
    // 2.构建查询条件 where name = "Jack"
    QueryWrapper<User> wrapper = new QueryWrapper<User>().eq("username", "Jack");
    // 3.更新数据,user中非null字段都会作为set语句
    userMapper.update(user, wrapper);
}

(2)UpdateWrapper

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

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

SET的赋值结果是基于字段现有值的,这个时候就要利用UpdateWrapper中的setSql功能了,
前面的是直接设置2000,而这里是根据现有的balance去减200

@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 手写sql
            .in("id", ids); // WHERE id in (1, 2, 4)
        // 2.更新,注意第一个参数可以给null,也就是不填更新字段和数据,
    // 而是基于UpdateWrapper中的setSQL来更新
    userMapper.update(null, wrapper);
}

(3)LambdaQueryWrapper

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

  • LambdaQueryWrapper——也可以写作QueryWrapper.lambda()
  • LambdaUpdateWrapper
    分别对应QueryWrapper和UpdateWrapper
@Test
void testLambdaQueryWrapper() {
    // 1.构建条件 WHERE username LIKE "%o%" AND balance >= 1000
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.lambda()
            .select(User::getId, User::getUsername, User::getInfo, User::getBalance)//传字段对应的get函数
            .like(User::getUsername, "o")
            .ge(User::getBalance, 1000);
    // 2.查询
    List<User> users = userMapper.selectList(wrapper);
    users.forEach(System.out::println);
}

(4)自定义SQL

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

自定义SQL使用:
1、先用Wrapper构建where条件
2、再调用Mapper层自定义的方法,将构建好的where条件和sql需要的参数传进去

就是把wrapper传到自定义的方法中,wrapper中的条件使用特殊占位符标在SQL注解中,并且方法参数要有@Param(“ew”)或者 @Param(Constants.WAPPER)

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

Mapper里

package com.itheima.mp.mapper;

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);//用@Param注解声明wrapper变量名称,必须为ew的
}

Service接口

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

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

由于Service中经常需要定义与业务有关的自定义方法,因此我们不能直接使用IService,而是自定义Service接口,然后继承IService以拓展方法。同时,让自定义的Service实现类继承ServiceImpl,这样就不用自己实现IService中的接口了。

(1)基本用法:
在这里插入图片描述
(2)
在这里插入图片描述

一般

package com.itheima.mp.service;

public interface IUserService extends IService<User> {
    // 拓展自定义方法
}
package com.itheima.mp.service.impl;

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User>implements IUserService {
}

在实现接口继承ServiceImpl后
按照Restful风格编写Controller接口方法:

package com.itheima.mp.controller;

@Api(tags = "用户管理接口")
@RequiredArgsConstructor
@RestController
@RequestMapping("users")
public class UserController {

    private final IUserService userService;//注入

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

    @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.处理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.处理vo
        return BeanUtil.copyToList(users, UserVO.class);
    }
}

添加业务逻辑

带有业务逻辑的接口则需要在service中自定义实现了

e.g.:根据id扣减用户余额
这看起来是个简单修改功能,只要修改用户余额即可。但这个业务包含一些业务逻辑处理:

  • 判断用户状态是否正常
  • 判断用户余额是否充足

业务逻辑都要在service层来做,另外更新余额需要自定义SQL,要在mapper中来实现。因此,我们除了要编写controller以外,具体的业务还要在service和mapper中编写。

Controller:

@PutMapping("{id}/deduction/{money}")
@ApiOperation("扣减用户余额")
public void deductBalance(@PathVariable("id") Long id, @PathVariable("money")Integer money){
    userService.deductBalance(id, money);
}
package com.itheima.mp.service;

public interface IUserService extends IService<User> {
    void deductBalance(Long id, Integer money);
}
package com.itheima.mp.service.impl;

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
    @Override
    public void deductBalance(Long id, Integer money) {
        // 1.查询用户——调userservice的getById来查,因为已经继承了,直接用getById就行
        User user = getById(id);
        // 2.判断用户状态
        if (user == null || user.getStatus() == 2) {
            throw new RuntimeException("用户状态异常");
        }
        // 3.判断用户余额
        if (user.getBalance() < money) {
            throw new RuntimeException("用户余额不足");
        }
        // 4.扣减余额—— update tb_user  set money = money - ?
        baseMapper.deductMoneyById(id, money);
    }
}

Mapper:

@Update("UPDATE user SET balance = balance - #{money} WHERE id = #{id}")
void deductMoneyById(@Param("id") Long id, @Param("money") Integer money);

总结

简单的方法直接在Controller里调MP提供的Service方法
业务逻辑相对复杂,需要自己写业务的时候用自定义Service
当MP和Service提供的方法不 足以满足需求时自定义sql语句Mapper(e.g.update tb_user set money = money - ?)

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

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

相关文章

基于javaScript的冒泡排序

目录 一.前言 二.设计思路和原理 三.源代码展示 四. 案例运行结果 一.前言 冒泡排序简而言之&#xff0c;就是一种算法&#xff0c;能够把一系列的数据按照一定的顺序进行排列显示&#xff08;从小到大或从大到小&#xff09;。例如能够将数组[5,4,3,2,1]中的元素按照从小到…

优选算法之双指针(上)

目录 双指针&#xff08;利用数组下标来充当指针&#xff09;&#xff1a; 一、移动零 1.题目链接&#xff1a;283.移动零 2.题目描述&#xff1a; 3.解法&#xff08;快排的思想&#xff1a;数组划分区间 - 数组分两块&#xff09; &#x1f334;算法思路&#xff1a; …

2024.7.14周报

目录 摘要 ABSTRACT 一、文献阅读 一、题目 二、摘要 三、文献解读 一、Introduction 二、KINN框架 三、主要结果 四、Conclusion 二、KAN 一、KAN与MLP区别 二、KAN网络解析 三、激活函数参数化&#xff08;B-splines&#xff09; 三、网络架构代码 摘要 本周…

Vue1-Vue核心

目录 Vue简介 官网 介绍与描述 Vue的特点 与其它 JS 框架的关联 Vue周边库 初识Vue Vue模板语法 数据绑定 el与data的两种写法 MVVM模型 数据代理 回顾Object.defineProperty方法 何为数据代理 Vue中的数据代理 数据代理图示 事件处理 事件的基本使用 事件修…

【Python 项目】类鸟群:仿真鸟群

类鸟群&#xff1a;仿真鸟群 仔细观察一群鸟或一群鱼&#xff0c;你会发现&#xff0c;虽然群体由个体生物组成&#xff0c;但该群体作为一个整体似乎有它自己的生命。鸟群中的鸟在移动、飞越和绕过障碍物时&#xff0c;彼此之间相互定位。受到打扰或惊吓时会破坏编队&#xf…

数据库管理-第218期 服务器内存(20240711)

数据库管理218期 2024-07-11 数据库管理-第218期 服务器内存&#xff08;20240711&#xff09;1 内存2 ECC内存3 原理3.1 多副本传输3.2 纠错码3.3 汉明码 总结 数据库管理-第218期 服务器内存&#xff08;20240711&#xff09; 作者&#xff1a;胖头鱼的鱼缸&#xff08;尹海文…

TCP三次握手四次挥手原理

学习视频&#xff1a;TCP三次握手四次挥手 TCP报文格式 源端口号和目的端口号&#xff1a;同IP数据报中的源IP与目的IP唯一确定一条TCP连接 序号seq&#xff08;4字节32位&#xff09;:用来标识TCP发端向TCP收端发送的数据字节流 确认序号&#xff08;4字节32位&#xff09;&a…

5.SpringBoot核心源码-启动类源码分析

目录 概述技巧spring boot 如何启动应用程序run方法里面核心逻辑 SpringApplicaiton.run(xxx.class,args)结束 概述 SpringBoot核心源码-启动类源码分析 技巧 如何给外部源码加注释&#xff0c;想要在源码中添加自己的注释&#xff0c;会弹出 file is read only&#xff0c;代…

光电门与电压放大器做电磁感应定律验证实验

根据上述原理可知&#xff0c;实验需要测量的量有两个&#xff1a;挡光时间间隔和挡光时的电压平均值。挡光时间可通过Arduino的数字输入端口采集光电门信号并计算得到&#xff0c;电压值可以通过Arduino的模拟信号输入端口采集。但是由于Arduino的模拟信号输入端只能精确到5mV…

【微信小程序开发】如何定义公共的js函数,其它页面可以调用

在微信小程序开发中&#xff0c;可以通过以下步骤定义和使用公共的 JS 函数&#xff0c;使得其它页面可以调用&#xff1a; 1. 创建一个公共的 JS 文件&#xff1a;在项目的 utils 目录下创建一个 JS 文件&#xff0c;例如 utils/util.js。 2. 定义公共函数&#xff1a;在 uti…

【Python从入门到进阶】60、Pandas中DataFrame对象的操作(一)

接上篇《59、Pandas库中Series对象的操作(二)》 上一篇我们讲解了Series对象的运算、函数应用、时间序列操作&#xff0c;以及Series的案例实践。本篇我们来讲解Pandas中DataFrame对象的操作。 一、DataFrame对象的基本概念及特点 在Pandas库中&#xff0c;DataFrame是一种非…

基于Python+Django+MySQL的心理咨询预约系统

心理咨询预约系统 DjangoMySQL 基于PythonDjangoMySQL的心理咨询预约系统 项目主要依赖Django3.2&#xff0c;MySQL 支持随机验证码生成与登录验证 简介 基于PythonDjangoMySQL的心理咨询预约系统通过连接数据库获取数据&#xff0c;登录新增随机数字验证码验证。具体可以看…

网络请求优化:如何让你的API飞起来

网络请求优化&#xff1a;如何让你的API飞起来 亲爱的开发者朋友们&#xff0c;你是否曾经遇到过这样的场景:用户疯狂点击刷新按钮,你的服务器却像老年人散步一样慢吞吞地响应。或者,你的应用像个贪吃蛇,疯狂吞噬用户的流量包。如果你对这些情况再熟悉不过,那么恭喜你,你正需要…

【Linux】进程间通信(IPC)——匿名管道

目录 为什么要进行进程间通信&#xff1f; 匿名管道的具体实现 pipe创建内存级文件形成管道 pipe的简单使用 匿名管道的四种情况和五种特性 四种情况 五种特性 PIPE_BUF 命令行管道 | 功能代码&#xff1a;创建进程池 为什么要进行进程间通信&#xff1f; 1.数据传输&…

1.27、基于径向基神经网络的曲线拟合(matlab)

1、基于径向基神经网络的曲线拟合简介及原理 1)原理简介 基于径向基神经网络(Radial Basis Function Neural Network, RBFNN)的曲线拟合是一种常用的非线性拟合方法,通过在输入空间中使用径向基函数对数据进行处理,实现对非线性关系的拟合。 RBFNN的基本原理是将输入空…

opencascade AIS_InteractiveContext源码学习8 trihedron display attributes

AIS_InteractiveContext 前言 交互上下文&#xff08;Interactive Context&#xff09;允许您在一个或多个视图器中管理交互对象的图形行为和选择。类方法使这一操作非常透明。需要记住的是&#xff0c;对于已经被交互上下文识别的交互对象&#xff0c;必须使用上下文方法进行…

Hash表(C++)

本篇将会开始介绍有关于 unordered_map 和 unordered_set 的底层原理&#xff0c;其中底层实现其实就是我们的 Hash 表&#xff0c;本篇将会讲解两种 Hash 表&#xff0c;其中一种为开放定址法&#xff0c;另一种为 hash 桶&#xff0c;在unordered_map 和 unordered_set 的底层…

【Git从入门到精通】——Git常用命令总结

&#x1f3bc;个人主页&#xff1a;【Y小夜】 &#x1f60e;作者简介&#xff1a;一位双非学校的大二学生&#xff0c;编程爱好者&#xff0c; 专注于基础和实战分享&#xff0c;欢迎私信咨询&#xff01; &#x1f386;入门专栏&#xff1a;&#x1f387;【MySQL&#xff0…

火星全球彩色影像图介绍(中分辨率相机)

一、数据基本信息 该数据是利用天问一号轨道器中分辨率相机获取的影像经光度校正、几何校正、全球制图等制作而成的全火星地图数据DOM&#xff0c;每个数据包含一个tif数据文件。该影像图分辨率为76米。 任务型号&#xff1a;天问一号 搭载平台&#xff1a;环绕器 数据获…

批量提取网页表格内容至excel文件

问题背景 将网页的表格内容&#xff08;5237个股票信息&#xff09;复制粘贴到excel文件中 网址&#xff1a;A股上市公司名单-A股上市公司名录-A股上市公司大全-商业计划书-可研报告-中商产业研究院数据库-中商情报网 实现代码 # 导入包 import pandas as pd import time# 创…