MyBatisPlus - 润物无声、效率至上、丰富功能

目录

一、简介 

1.1、为什么要使用 MybatisPlus

二、使用指南

2.1、依赖

2.2、配置

2.3、常用注解

2.4、BaseMapper 的使用

2.4.1、定义 Mapper 接口

2.4.2、基于 QueryWrapper 的查询(不推荐)

2.4.3、基于 UpdateWrapper 的修改(不推荐)

2.4.4、基于 Lambda 的查询、修改(推荐)

2.5、自定义 SQL

2.6、IService 和 ServiceImpl

2.7、Db 静态工具

2.8、分页插件

2.8.1、配置类

2.8.2、实际使用


一、简介 


1.1、为什么要使用 MybatisPlus

a)润物无声:

在 MyBatis 基础上只做增强,不做修改,不会对现有工程产生影响,并且在启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作.

b)效率至上:

MP 提供了很多现成的单表操作,只需简单配置,就可以进行单表的 CRUD,节省大量时间.  另外还提供了 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错.

c)扩展功能:

代码生成、自动分页、逻辑删除、自动填充等等.... 但是我觉得这里有些东西可能自己基建更合适.

二、使用指南


2.1、依赖

MyBatisPlus 的 starter 可以直接替代 MyBatis 的 starter

        <dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.3.1</version></dependency>

2.2、配置

mybatis-plus:type-aliases-package: com/cyk/mp/model/domain # 别名扫描包# 以下配置都是默认值,可以根据项目情况进行修改mapper-locations: "classpath*:/mapper/**/*.xml"  # Mapper.xml 文件地址configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #打印 sqlmap-underscore-to-camel-case: true # 是否开启下划线和驼峰的映射cache-enabled: false # 是否开启二级缓存global-config:db-config:id-type: assign_id # 默认雪花算法,另外还有 auto(自增id) assign_uuid(uuid)update-strategy: not_null # 更新策略: 只更新非空字段

2.3、常用注解

a)常用注解如下:

  • @TableName:用来指定表名.
  • @TableId:用来指定表中的主键字段信息.
  • @TableField:用来指定表中的普通字段信息.

具体的注解参数可以参考官网:注解 | MyBatis-Plus

例如 user_info 表如下:

类定义如下:

@Data
@TableName(value = "user_info", autoResultMap = true)
public class User {@TableId(value = "id", type = IdType.AUTO) //value 默认是 id, type 默认是雪花算法,这里修改为自增长private Long id;@TableField("username")private String name;@TableField("is_married")private Boolean isMarried;private Integer age;//这里会自动驼峰转下划线private Date ctTime;private Date utTime;}

Ps:此案例贯穿全文 

b)注意事项如下

@TableField 中若指定 value 生效的前提是: @TableName 中 autoResultMap = true,此设置会按照 TableField 中的设置的 value 自动生成 resultMap.

IdType 枚举类型如下

  • AUTO:数据库 ID 自增.
  • NONE:无状态,该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT)
  • INPUT:通过 set 方法自行注入.
  • ASSIGN_ID:分配 ID(主键类型为 Number(Long 和 Integer)或 String),接口 IdentifierGenerator 的方法 nextid 来生成 id,默认是雪花算法.
  • ASSIGN_UUID:分配 UUID,主键类型为 String

使用 @TableASSIGN_UUIDField 的常见场景:

  • 成员变量与数据库字段名不一致
  • 成员变量名以 is 开头,且是布尔值.
  • 成员变量名与数据库关键字冲突(反引号解决)
  • 成员变量不是数据库字段(@TableField(exist = false) 表示表中不存在该字段,用来排除).

2.4、BaseMapper<T> 的使用

2.4.1、定义 Mapper 接口

自定义的 Mapper 类继承 MyBatisPlus 提供的 BaseMapper 接口:

例如对 User 类操作.

public interface UserMapper extends BaseMapper<User> {}

可以看到 BaseMapper 接口中提供了很多方法,都可以直接调用~

2.4.2、基于 QueryWrapper 的查询(不推荐)

QueryWrapper 通常用来构建 select、delete 的 select 和 where 条件部分,以及 update 的 where 部分.

a)案例一:查询出 name 中带 y 的,age 小于 30 的( id、username、age、is_married )信息.

MP 实现如下:

    @Testpublic void test1() {//1.构造查询条件(支持链式编程)QueryWrapper<User> wrapper = new QueryWrapper<User>().select("id", "username", "age", "is_married").like("username", "y").lt("age", 30); // ge大于等于 gt大于 lt小于 le小于等于//2.查询List<User> users = userMapper.selectList(wrapper);users.forEach(System.out::println);}

MP 实际生成的 SQL 语句如下:

SELECT id,username,age,is_married FROM user_info WHERE (username LIKE ? AND age < ?)

b)案例二:更新 username 为 cyk 的 is_married 字段为 true.

MP 实现如下:

    @Testpublic void test2() {//1.要更新的数据User user = new User();user.setIsMarried(true);//2.构造更新条件QueryWrapper<User> wrapper = new QueryWrapper<User>().eq("username", "cyk");//3.执行更新userMapper.update(user, wrapper);}

MP 实际生成的 SQL 语句如下:

UPDATE user_info SET is_married=? WHERE (username = ?)

2.4.3、基于 UpdateWrapper 的修改(不推荐)

UpdateWrapper 通常用来构造 update 语句的 set 和 where 部分.

a)案例一:让 id 为 1,2,3 的用户的 age 加 10.

MP 实现如下:

    @Testpublic void test3() {List<Long> ids = List.of(1L,2L,3L);UpdateWrapper<User> wrapper = new UpdateWrapper<User>().setSql("age = age + 10").in("id", ids);userMapper.update(null, wrapper);}

MP 实际生成的 SQL 语句如下: 

 UPDATE user_info SET age = age + 10 WHERE (id IN (?,?,?))

2.4.4、基于 Lambda 的查询、修改(推荐)

上面提到的这两种方式实际上是硬编码的写法(不推荐),下面讲的 Lambda 写法才是最合适的.

a)案例一:查询出 name 中带 y 的,age 小于 30 的( id、username、age、is_married )信息.

MP 实现如下:

    @Testpublic void test4() {LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<User>().select(User::getId, User::getName, User::getAge, User::getIsMarried).lt(User::getAge, 30);List<User> users = userMapper.selectList(wrapper);users.forEach(System.out::println);}

MP 底层执行的 sql 如下: 

SELECT id,username,age,is_married FROM user_info WHERE (age < ?)

 b)案例二:在公司经常会遇到一个接口复用多个接口的情况,例如根据 ids、name、is_married 查询用户信息,但是这三个参数都有可能为空.  这里 MP 能很好的抛弃 if 语句,十分优雅

    @Testpublic void test6() {//1.假设前端传入请求数据UserDTO dto = new UserDTO();dto.setIds(List.of(1L,2L,3L));dto.setName(null);dto.setAgeLeft(10);dto.setAgeRight(30);//2.构造请求LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<User>().in(!dto.getIds().isEmpty(), User::getId, dto.getIds()).eq(StringUtils.isNotBlank(dto.getName()), User::getName, dto.getName()).between(dto.getAgeLeft() != null && dto.getAgeRight() != null,User::getAge, dto.getAgeLeft(), dto.getAgeRight());//3.执行查询List<User> users = userMapper.selectList(wrapper);users.forEach(System.out::println);}

 MP 底层执行的 sql 如下: 

SELECT id,username,is_married,age,ct_time,ut_time FROM user_info WHERE (id IN (?,?,?) AND age BETWEEN ? AND ?)

2.5、自定义 SQL

MyBatisPlus 更适合于构建复杂的 where 的条件(避免使用一些 动态 sql 显得臃肿),对于例如 update 中出现例如 "set age = age + 10" 这样的语句就只能使用硬编码.

而 MyBatisPlus 是包含 MyBatis 的,因此可以通过 MyBatis 定义 sql 语句中剩下的部分.

总而言之:对于复杂 update 语句,自定义 where 条件之前的语句,MP 构建 where 条件之后的语句

案例:让 id 为 1,2,3 的用户的 age 加 10.

a)基于 Wrapper 构建 where 条件

        //让 age 增加 10int addAge = 10;//要修改哪些用户List<Long> ids = List.of(1L, 2L, 3L);LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<User>().in(User::getId, ids);//updateBatchByIds 是自定义的方法userMapper.updateBatchByIds(wrapper, addAge);

b)在 mapper 方法参数中用 Param 注解声明 wrapper 变量名称,必须是 ew 

@Mapper
public interface UserMapper extends BaseMapper<User> {//wrapper 的变量声明必须是 ewvoid updateBatchByIds(@Param("ew") LambdaQueryWrapper<User> wrapper,@Param("addAge") int addAge);}

如果忘记是 ew 这个东西,也可以通过 Constants 这个常量来找到.

 

c)在 xml 文件中自定义 sql,并使用 Wrapper 条件

    <update id="updateBatchByIds">update user_infoset age = age + #{addAge} ${ew.customSqlSegment}</update>

MP 实际生成 sql 语句如下:

 update user_info set age = age + ? WHERE (id IN (?,?,?))

2.6、IService 和 ServiceImpl

Ps:不太建议在 Service 层乱造,可以自己封装以下... 当然 BaseMapper 也够用.

IService 接口提供中也提供了很多的 方法,并在 ServiceImpl 也给出了具体的实现.

这里的方法和上面讲到的 BaseMapper 大差不差,就不展开讲了.

下面以 User 类为例.

a)首先自定义 mapper 接口,继承 BaseMapper

@Mapper
public interface UserMapper extends BaseMapper<User> {}

a)我们只需要在我们自定义的 IXXXService 上继承 IService

public interface IUserService extends IService<User> {}

b)并在自定义的 XXXServiceImpl 上继承 ServiceImpl 即可

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

d)之后这些方法都可以随便用了,例如 getById

    @Overridepublic void xxxMethod(Long id) {User byId = getById(id);//...}

e)当然,这里也提供了对应的 lambda 表达式

    @Overridepublic List<User> query(QueryUserDTO dto) {return lambdaQuery().in(!dto.getIds().isEmpty(), User::getId, dto.getIds()).eq(StringUtils.hasLength(dto.getName()), User::getName, dto.getName()).between(dto.getMinAge() != null && dto.getMaxAge() != null,User::getAge, dto.getMinAge(), dto.getMaxAge()).list(); //生成一个 list    当然也可以选择生成 page 后面会讲}

模拟创建请求调用 query 方法如下:

        QueryUserDTO dto = QueryUserDTO.builder().ids(List.of(1L,2L,3L)).name(null).minAge(10).maxAge(30).build();List<User> users = userService.query(dto);users.forEach(System.out::println);

 MP 实际执行的 sql 语句

SELECT id,username,is_married,age,ct_time,ut_time FROM user_info WHERE (id IN (?,?,?) AND age BETWEEN ? AND ?)

2.7、Db 静态工具

可以看到和 IService 中方法几乎一摸一样,只不过 IService 接口里面的方法都是非静态 ,而 DB 这个静态工具里面的方法自然也都是静态的

而且可以看到一些细小的变化,他的方法除了新增和修改以外都需要指定泛型,这是因为他需要通过反射才能拿到实体类的字节码,才知道你的表信息.

那这玩意到底和 IService 有什么区别呢?

假设有两张表,用户表和地址表,并且业务需要根据用户 id 查询地址,又需要根据地址查询用户id,那么就你就可能去 UserService 中注入 AddressService ,而又在 AddressService 中注入 UserService,这样就产生了循环依赖问题.

虽然循环依赖在较新的 Spring 版本中已被解决,但是最好还是需要我们开发人员去注意这个问题,保证系统的稳定性.

此时就可以通过 DB 静态工具类来实现了. 

2.8、分页插件

2.8.1、配置类

首先,要在配置类种注册 MyBatisPlus 的核心插件,同时添加分页插件:

@Configuration
public class MybatisConfig {@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {//1.初始化核心插件MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();//2.创建分页插件PaginationInnerInterceptor page = new PaginationInnerInterceptor(DbType.MYSQL);//设置分页上线page.setMaxLimit(100L);//3.添加分页插件到 MybatisPlusinterceptor.addInnerInterceptor(page);return interceptor;}}

Ps:缺少以上配置,就没法使用分页.  这个插件本质就是在执行指令之前被拦截器拦截下来, 添加分页命令.

2.8.2、实际使用

案例:在公司经常会遇到一个接口复用多个接口的情况,例如根据 ids、name、is_married 查询用户信息,但是这三个参数都有可能为空,最后的结果分页显示(limit 3 offset 0),并根据 id 升序排序.

Ps:在 Page 中若没有使用 addOrder 方法,或者方法参数为空,默认按照创建时间排序.

a)MP 实现如下:

        //1.默认请求PageUserDTO dto = PageUserDTO.builder().start(2).limit(1).ids(List.of(1L,2L,3L)).name(null).minAge(10).maxAge(30).build();//2.分页查询//2.1 构建查询LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<User>().in(!dto.getIds().isEmpty(), User::getId, dto.getIds()).eq(StringUtils.isNotBlank(dto.getName()), User::getName, dto.getName()).between(dto.getMinAge() != null && dto.getMaxAge() != null, User::getAge, dto.getMinAge(), dto.getMaxAge());//2.2 构建分页Page<User> page = Page.of(dto.getStart(), dto.getLimit()); //起始页码(非下标), 每页显示条数page.addOrder(new OrderItem("id", true)); //true 表示升序//3.查询Page<User> result = userService.page(page, wrapper);//3.1 分页数据List<User> records = result.getRecords();//3.2 总条数long total = result.getTotal();//3.3 总页数long pages = result.getPages();

MP 底层执行的 sql 如下:

SELECT COUNT(*) AS total FROM user_info WHERE (id IN (?, ?, ?) AND age BETWEEN ? AND ?)SELECT id, username, is_married, age, ct_time, ut_time FROM user_info WHERE (id IN (?, ?, ?) AND age BETWEEN ? AND ?) ORDER BY id ASC LIMIT ?,?

 

b)但是实际开发一般也不会写的像上述代码一样这么麻烦,更多的可能是这样

        //1.默认请求PageUserDTO dto = PageUserDTO.builder().start(2).limit(1).ids(List.of(1L,2L,3L)).name(null).minAge(10).maxAge(30).build();//2.构建分页条件Page<User> page = Page.of(dto.getStart(), dto.getLimit()); //起始页码(非下标), 每页显示条数page.addOrder(new OrderItem("id", true)); //true 表示升序//3.查询Page<User> result = userService.lambdaQuery().in(!dto.getIds().isEmpty(), User::getId, dto.getIds()).eq(StringUtils.isNotBlank(dto.getName()), User::getName, dto.getName()).between(dto.getMinAge() != null && dto.getMaxAge() != null, User::getAge, dto.getMinAge(), dto.getMaxAge()).page(page);//3.1 分页数据List<User> records = result.getRecords();//3.2 总条数long total = result.getTotal();//3.3 总页数long pages = result.getPages();

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

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

相关文章

Sentinel 流控-链路模式

链路模式 A B C 三个服务 A 调用 C B 调用 C C 设置流控 ->链路模式 -> 入口资源是 A A、B 服务 package com.learning.springcloud.order.controller;import com.learning.springcloud.order.service.BaseService; import org.springframework.beans.factory.annotatio…

腾讯云4核8G服务器3年600元?

腾讯云4核8G服务器3年600元&#xff1f;目前的价格是轻量应用服务器4核8G12M带宽一年446元、646元15个月&#xff0c;云服务器CVM标准型S5实例4核8G配置价格15个月1437.3元&#xff0c;5年6490.44元&#xff0c;标准型SA2服务器1444.8元一年&#xff0c;在txy.wiki可以查询详细…

Excel一键导入导出-EasyPOI

EasyPOI是一款优秀的开源Java库&#xff0c;专为简化和优化Excel文件的导入导出操作而设计。下面&#xff0c;我会介绍EasyPOI在项目中使用EasyPOI&#xff0c;实现Excel文件的高效操作。帮助读者全面了解和掌握这一工具。 EasyPOI简介 官网&#xff1a; http://www.wupaas.co…

windows服务启动

一.NetCore 1.创建启动脚本run_instal.bat,例如程序文件为ApiDoc.exe set serviceName"Apidoc Web 01" set serviceFilePath%~dp0ApiDoc.exe set serviceDescription"ApiDoc 动态接口服务 web 01"sc create %serviceName% BinPath%serviceFilePath% sc c…

如何在Django中使用分布式定时任务并结合消息队列

如何在Django中使用分布式定时任务并结合消息队列 如何在Django中使用分布式定时任务并结合消息队列项目背景与意义实现步骤1. 安装Celery和Django-celery-beat2. 配置Celery3. 配置Django-celery-beat4. 定义定时任务5. 启动Celery worker 和 beat6. Celery 指令7. 对接消息队…

「软件设计师」操作系统基本原理

操作系统概述 操作系统与计算机体系结构之间的关系 操作系统具备的管理职能 进程管理 进程的状态前趋图pv操作死锁问题存储管理 段页式存储页面置换算法文件管理 索引文件位示图作业管理设备管理 数据传输控制方式微内核操作系统 虚设备与SPOOLING技术 进程管理 进程的状态…

【OrangePi Zero2 智能家居】智能家居项目的软件实现

一、项目整体设计 二、项目代码的前期准备 三、实现语音监听接口 四、实现socket监听接口 五、实现烟雾报警监听接口 六、实现设备节点代码 七、实现接收消息处理接口 一、项目整体设计 整体的软件框架大致如下&#xff1a; 整个项目开启4个监听线程&#xff0c; 分别是&…

奇异值分解(SVD)

对于一个方阵而言&#xff0c;采用的是特征分解&#xff0c;参考《矩阵特征值分解&#xff08;EVD&#xff09;-CSDN博客》

高程 | 数据的共享与保护(c++)

文章目录 &#x1f4da;标识符的作用域与可见性&#x1f407;作用域&#x1f407;可见性 &#x1f4da;对象的生存期&#x1f407;静态生存期&#x1f407;动态生存期 &#x1f4da;类的静态成员&#x1f407;静态数据成员&#x1f407;静态函数成员 &#x1f4da;类的友元&…

你的电脑关机吗

目录 程序员为什么不喜欢关电脑&#xff1f; 电脑长时间不关机会怎样? 电脑卡顿 中度风险 硬件损耗 能源浪费 散热问题 软件问题 网络安全问题 程序员为什么不喜欢关电脑&#xff1f; 大部分人都会选择将电脑进行关机操作。其实这不难理解&#xff0c;毕竟人类都需要…

MyBatis篇----第五篇

系列文章目录 文章目录 系列文章目录前言一、MyBatis 实现一对一有几种方式?具体怎么操作的?二、MyBatis 实现一对多有几种方式,怎么操作的?三、Mybatis 是否支持延迟加载?如果支持,它的实现原理是什么?四、Mybatis 的一级、二级缓存前言 前些天发现了一个巨牛的人工智能…

【数据库_MySQL】MySQL彻底卸载

程序员为什么不喜欢关电脑&#xff1f; 你是否注意到&#xff0c;程序员们似乎从不关电脑&#xff1f;别以为他们是电脑上瘾&#xff0c;实则是有他们自己的原因&#xff01;让我们一起揭秘背后的原因&#xff0c;看看程序员们真正的“英雄”本色&#xff01; 卸载 要是你的…

【机器学习案例3】从科学论文图片中提取标题、作者和摘要【含源码】

在这个项目中,我的目标是从科学论文图片中提取某些部分(标题、作者和摘要)。预期提取部分是科学论文中常见的部分,例如标题、摘要和作者。输入与最终结果。我的输入是将第一页纸转换成图像。最终结果是一个 txt 文件,其中包含标题、作者和摘要部分,如下图1和图2所示。我将…

SpringBoot整合第三方技术-缓存

&#x1f648;作者简介&#xff1a;练习时长两年半的Java up主 &#x1f649;个人主页&#xff1a;程序员老茶 &#x1f64a; ps:点赞&#x1f44d;是免费的&#xff0c;却可以让写博客的作者开心好久好久&#x1f60e; &#x1f4da;系列专栏&#xff1a;Java全栈&#xff0c;…

每日OJ题_递归①_力扣面试题 08.06. 汉诺塔问题

目录 递归算法原理 力扣面试题 08.06. 汉诺塔问题 解析代码 递归算法原理 递归算法个人经验&#xff1a;给定一个任务&#xff0c;相信递归函数一定能解决这个任务&#xff0c;根据任务所需的东西&#xff0c;给出函数参数&#xff0c;然后实现函数内容&#xff0c;最后找出…

linux内核原理--用户态线性地址空间,mmap,malloc,缺页异常

1.概述 前面我们介绍了内核态线性地址空间划分&#xff0c;及在内核态运行时&#xff0c;如何利用伙伴系统完成连续可用物理页框申请和释放。如何利用小块内存分配器实现高效的动态内存分配和释放。如何利用vmalloc&#xff0c;vfree完成线性地址连续但物理地址不连续的多个页框…

什么是 Flet?

什么是 Flet&#xff1f; Flet 是一个框架&#xff0c;允许使用您喜欢的语言构建交互式多用户 Web、桌面和移动应用程序&#xff0c;而无需前端开发经验。 您可以使用基于 Google 的 Flutter 的 Flet 控件为程序构建 UI。Flet 不只是“包装”Flutter 小部件&#xff0c;而是…

上位机图像处理和嵌入式模块部署(上位机主要功能)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 目前关于机器视觉方面&#xff0c;相关的软件很多。比如说商业化的halcon、vision pro、vision master&#xff0c;当然也可以用opencv、pytorch自…

使用 Chainlit, Langchain 及 Elasticsearch 轻松实现对 PDF 文件的查询

在我之前的文章 “Elasticsearch&#xff1a;与多个 PDF 聊天 | LangChain Python 应用教程&#xff08;免费 LLMs 和嵌入&#xff09;” 里&#xff0c;我详述如何使用 Streamlit&#xff0c;Langchain, Elasticsearch 及 OpenAI 来针对 PDF 进行聊天。在今天的文章中&#xf…

[缓存] - 2.分布式缓存重磅中间件 Redis

1. 高性能 尽量使用短key 不要存过大的数据 避免使用keys *&#xff1a;使用SCAN,来代替 在存到Redis之前压缩数据 设置 key 有效期 选择回收策略(maxmemory-policy) 减少不必要的连接 限制redis的内存大小&#xff08;防止swap&#xff0c;OOM&#xff09; slowLog …