当前位置: 首页 > news >正文

深入剖析BaseMapperPlus扩展接口及其在MyBatis-Plus中的实践价值

前言

BaseMapperPlus并非MyBatis-Plus(MP)官方提供的标准接口,而是社区开发者基于MP的BaseMapper接口进行二次封装和增强后创建的一个自定义接口。这个概念可能因不同项目或个人实践而有所差异,但其核心思想是为了解决特定场景下的需求,进一步简化数据库操作,并提高开发效率。
代码示例

/*** 自定义 Mapper 接口, 实现 自定义扩展** @param <M> mapper 泛型* @param <T> table 泛型* @param <V> vo 泛型* @author xiaoli* @since 2024/1/24*/
@SuppressWarnings("unchecked")
public interface BaseMapperPlus<M, T, V> extends BaseMapper<T> {Log log = LogFactory.getLog(BaseMapperPlus.class);int DEFAULT_BATCH_SIZE = 1000;default Class<V> currentVoClass() {return (Class<V>) ReflectionKit.getSuperClassGenericType(this.getClass(), BaseMapperPlus.class, 2);}default Class<T> currentModelClass() {return (Class<T>) ReflectionKit.getSuperClassGenericType(this.getClass(), BaseMapperPlus.class, 1);}default Class<M> currentMapperClass() {return (Class<M>) ReflectionKit.getSuperClassGenericType(this.getClass(), BaseMapperPlus.class, 0);}default List<T> selectList() {return this.selectList(new QueryWrapper<>());}/*** 批量插入*/default boolean insertBatch(Collection<T> entityList) {return insertBatch(entityList, DEFAULT_BATCH_SIZE);}/*** 批量更新*/default boolean updateBatchById(Collection<T> entityList) {return updateBatchById(entityList, DEFAULT_BATCH_SIZE);}/*** 批量插入或更新*/default boolean insertOrUpdateBatch(Collection<T> entityList) {return insertOrUpdateBatch(entityList, DEFAULT_BATCH_SIZE);}/*** 批量插入(包含限制条数)*/default boolean insertBatch(Collection<T> entityList, int batchSize) {String sqlStatement = SqlHelper.getSqlStatement(this.currentMapperClass(), SqlMethod.INSERT_ONE);return SqlHelper.executeBatch(this.currentModelClass(), log, entityList, batchSize,(sqlSession, entity) -> sqlSession.insert(sqlStatement, entity));}/*** 批量更新(包含限制条数)*/default boolean updateBatchById(Collection<T> entityList, int batchSize) {String sqlStatement = SqlHelper.getSqlStatement(this.currentMapperClass(), SqlMethod.UPDATE_BY_ID);return SqlHelper.executeBatch(this.currentModelClass(), log, entityList, batchSize,(sqlSession, entity) -> {MapperMethod.ParamMap<T> param = new MapperMethod.ParamMap<>();param.put(Constants.ENTITY, entity);sqlSession.update(sqlStatement, param);});}/*** 批量插入或更新(包含限制条数)*/default boolean insertOrUpdateBatch(Collection<T> entityList, int batchSize) {TableInfo tableInfo = TableInfoHelper.getTableInfo(this.currentModelClass());Assert.notNull(tableInfo, "error: can not execute. because can not find cache of TableInfo for entity!");String keyProperty = tableInfo.getKeyProperty();Assert.notEmpty(keyProperty, "error: can not execute. because can not find column for id from entity!");return SqlHelper.saveOrUpdateBatch(this.currentModelClass(), this.currentMapperClass(), log, entityList, batchSize, (sqlSession, entity) -> {Object idVal = tableInfo.getPropertyValue(entity, keyProperty);String sqlStatement = SqlHelper.getSqlStatement(this.currentMapperClass(), SqlMethod.SELECT_BY_ID);return StringUtils.checkValNull(idVal)|| CollectionUtils.isEmpty(sqlSession.selectList(sqlStatement, entity));}, (sqlSession, entity) -> {MapperMethod.ParamMap<T> param = new MapperMethod.ParamMap<>();param.put(Constants.ENTITY, entity);String sqlStatement = SqlHelper.getSqlStatement(this.currentMapperClass(), SqlMethod.UPDATE_BY_ID);sqlSession.update(sqlStatement, param);});}/*** 插入或更新(包含限制条数)*/default boolean insertOrUpdate(T entity) {if (null != entity) {TableInfo tableInfo = TableInfoHelper.getTableInfo(this.currentModelClass());Assert.notNull(tableInfo, "error: can not execute. because can not find cache of TableInfo for entity!");String keyProperty = tableInfo.getKeyProperty();Assert.notEmpty(keyProperty, "error: can not execute. because can not find column for id from entity!");Object idVal = tableInfo.getPropertyValue(entity, tableInfo.getKeyProperty());return StringUtils.checkValNull(idVal) || Objects.isNull(selectById((Serializable) idVal)) ? insert(entity) > 0 : updateById(entity) > 0;}return false;}default V selectVoById(Serializable id) {return selectVoById(id, this.currentVoClass());}/*** 根据 ID 查询*/default <C> C selectVoById(Serializable id, Class<C> voClass) {T obj = this.selectById(id);if (ObjectUtil.isNull(obj)) {return null;}return BeanCopyUtils.copy(obj, voClass);}default List<V> selectVoById(Collection<? extends Serializable> idList) {return selectVoBatchIds(idList, this.currentVoClass());}/*** 查询(根据ID 批量查询)*/default <C> List<C> selectVoBatchIds(Collection<? extends Serializable> idList, Class<C> voClass) {List<T> list = this.selectBatchIds(idList);if (CollUtil.isEmpty(list)) {return CollUtil.newArrayList();}return BeanCopyUtils.copyList(list, voClass);}default List<V> selectVoByMap(Map<String, Object> map) {return selectVoByMap(map, this.currentVoClass());}/*** 查询(根据 columnMap 条件)*/default <C> List<C> selectVoByMap(Map<String, Object> map, Class<C> voClass) {List<T> list = this.selectByMap(map);if (CollUtil.isEmpty(list)) {return CollUtil.newArrayList();}return BeanCopyUtils.copyList(list, voClass);}default V selectVoOne(Wrapper<T> wrapper) {return selectVoOne(wrapper, this.currentVoClass());}/*** 根据 entity 条件,查询一条记录*/default <C> C selectVoOne(Wrapper<T> wrapper, Class<C> voClass) {T obj = this.selectOne(wrapper);if (ObjectUtil.isNull(obj)) {return null;}return BeanCopyUtils.copy(obj, voClass);}default List<V> selectVoList(Wrapper<T> wrapper) {return selectVoList(wrapper, this.currentVoClass());}/*** 根据 entity 条件,查询全部记录*/default <C> List<C> selectVoList(Wrapper<T> wrapper, Class<C> voClass) {List<T> list = this.selectList(wrapper);if (CollUtil.isEmpty(list)) {return CollUtil.newArrayList();}return BeanCopyUtils.copyList(list, voClass);}default <P extends IPage<V>> P selectVoPage(IPage<T> page, Wrapper<T> wrapper) {return selectVoPage(page, wrapper, this.currentVoClass());}/*** 分页查询VO*/default <C, P extends IPage<C>> P selectVoPage(IPage<T> page, Wrapper<T> wrapper, Class<C> voClass) {IPage<T> pageData = this.selectPage(page, wrapper);IPage<C> voPage = new Page<>(pageData.getCurrent(), pageData.getSize(), pageData.getTotal());if (CollUtil.isEmpty(pageData.getRecords())) {return (P) voPage;}voPage.setRecords(BeanCopyUtils.copyList(pageData.getRecords(), voClass));return (P) voPage;}}

使用方法示例

// 定义实体类和Vo类
public class User {private Long id;private String username;// 其他属性、getter/setter省略...
}public class UserVo {private Long id;private String username;// 其他属性、getter/setter省略...
}// 自定义扩展的Mapper接口
public interface UserMapper extends BaseMapperPlus<UserMapper, User, UserVo> {// 可以添加更多针对User的自定义查询方法
}// Mapper接口的实现由MyBatis框架自动生成,无需手动编写// 服务层接口
public interface IUserService {UserVo queryById(Long id);// 其他业务方法...
}// 服务层实现类
@Service
@RequiredArgsConstructor
public class UserServiceImpl implements IUserService {/** 用户Mapper接口 */private final UserMapper userMapper;/*** @Description: 根据ID查询用户信息,并转换为VO对象* @author: luojianmin (此处应替换为实际作者)* @date: 2022/6/23 9:32(此处应替换为实际日期)* @param id 用户主键* @return 用户信息VO对象*/@Overridepublic UserVo queryById(Long id) {return userMapper.selectVoById(id);}// 其他业务逻辑的实现...
}

一、BaseMapperPlus的独特作用与诞生背景

  1. 简化CRUD操作
    BaseMapper已经提供了基础的增删改查功能,然而,在实际业务开发中,我们可能会遇到更复杂的数据处理场景,比如批量插入更新、组合查询、分组统计等。BaseMapperPlus正是为了满足这些高级需求而设计,通过提供一系列便捷的方法,简化了数据操作层面上的代码编写。

  2. 定制化方法
    开发者可以根据项目特点,在BaseMapperPlus中添加一些通用的、适用于大部分实体类的方法,如根据多个字段联合查询、特殊条件下的删除操作等。这样,每个具体业务对象对应的mapper接口只需继承BaseMapperPlus就能获得丰富的预定义功能。

  3. 抽象复用
    通过创建BaseMapperPlus接口,可以将常见的数据库操作逻辑抽象出来,实现代码复用,降低重复开发成本,同时保持代码的整洁性和一致性。

二、继承BaseMapperPlus的好处

  1. 减少重复代码
    继承BaseMapperPlus后,可以直接调用其中预先定义好的方法,避免了在每个具体的mapper接口中手动编写相似的操作逻辑。

  2. 提升开发效率
    开发者无需关心底层SQL细节,仅需关注业务逻辑本身,能够更快地完成数据访问层的开发工作。

  3. 易于维护
    所有通用的数据库操作逻辑都被集中到了BaseMapperPlus中,一旦需要修改或优化,只需要在一个地方改动即可,有利于后期的维护和升级。

三、BaseMapperPlus示例方法及优势

虽然BaseMapperPlus的具体内容取决于项目的实际需求,但它通常会包含以下类型的方法:

  • 批量操作增强:如批量插入、批量更新、批量删除。
  • 条件查询增强:支持更复杂的组合查询、排序方式自定义、结果集过滤等功能。
  • 统计分析增强:提供分组统计、聚合函数使用、分页统计等功能。
  • 事务控制:如果框架本身不提供,则可以在BaseMapperPlus中加入对事务管理的支持。

相比原生的MyBatis-Plus,BaseMapperPlus的优势在于它更加贴近项目实际,具有更强的针对性和灵活性,能够解决更多个性化的需求,从而让开发者能专注于业务逻辑的实现,而不是底层数据操作的繁琐编码。

总结

尽管BaseMapperPlus不是MyBatis-Plus的标准组件,但在实际开发过程中,这样的扩展接口体现了极高的实用价值。它以一种可复用、易维护的方式增强了MyBatis-Plus的功能,使得开发者在面对各种复杂数据库操作时,能享受到更高程度的便利性。在构建项目时,建议结合具体业务需求来设计并实现一个符合自己团队习惯的BaseMapperPlus接口,从而最大化地发挥出它的价值。

相关文章:

深入剖析BaseMapperPlus扩展接口及其在MyBatis-Plus中的实践价值

前言 BaseMapperPlus并非MyBatis-Plus&#xff08;MP&#xff09;官方提供的标准接口&#xff0c;而是社区开发者基于MP的BaseMapper接口进行二次封装和增强后创建的一个自定义接口。这个概念可能因不同项目或个人实践而有所差异&#xff0c;但其核心思想是为了解决特定场景下…...

Linux之安装配置VCentOS7+换源

目录 一、安装 二、配置 三、安装工具XSHELL 3.1 使用XSHELL连接Linux 四、换源 前言 首先需要安装VMware虚拟机&#xff0c;在虚拟机里进行安装Linux 简介 Linux&#xff0c;一般指GNU/Linux&#xff08;单独的Linux内核并不可直接使用&#xff0c;一般搭配GNU套件&#…...

[极客大挑战 2019]LoveSQL1

万能密码测试&#xff0c;发现注入点 注意这里#要使用url编码才能正常注入 测试列数&#xff0c;得三列 查看table&#xff0c;一个是geekuser另一个是l0ve1ysq1 查看column&#xff0c;有id&#xff0c;username&#xff0c;password&#xff0c;全部打印出来&#xff0c;…...

网络安全的介绍

1.什么是网络安全 网络安全是一门关注保护计算机系统、网络基础设施和数据免受未经授权访问、破坏或窃取的学科。随着数字化时代的发展&#xff0c;网络安全变得尤为重要&#xff0c;因为大量的个人信息、商业机密和政府数据都储存在电子设备和云端系统中。以下是网络安全的概…...

django邮件通知功能-

需求&#xff1a; 1&#xff1a;下单人员下订单时需要向组长和投流手发送邮件通知 2&#xff1a;为何使用邮件通知功能&#xff1f;因为没钱去开通短信通知功能 设计 1&#xff1a;给用户信息表添加2个字段 第一个字段为&#xff1a;是否开通邮件通知的布尔值 第二个字段为: 用…...

C++ 类定义

C 类定义 定义一个类需要使用关键字 class&#xff0c;然后指定类的名称&#xff0c;并类的主体是包含在一对花括号中&#xff0c;主体包含类的成员变量和成员函数。 定义一个类&#xff0c;本质上是定义一个数据类型的蓝图&#xff0c;它定义了类的对象包括了什么&#xff0…...

IntelliJ IDE 插件开发 | (五)VFS 与编辑器

系列文章 IntelliJ IDE 插件开发 |&#xff08;一&#xff09;快速入门IntelliJ IDE 插件开发 |&#xff08;二&#xff09;UI 界面与数据持久化IntelliJ IDE 插件开发 |&#xff08;三&#xff09;消息通知与事件监听IntelliJ IDE 插件开发 |&#xff08;四&#xff09;来查收…...

金融OCR领域实习日志(一)

一、OCR基础 任务要求&#xff1a; 工作原理 OCR&#xff08;Optical Character Recognition&#xff0c;光学字符识别&#xff09;是指电子设备&#xff08;例如扫描仪或数码相&#xff09;检查纸上打印的字符&#xff0c;经过检测暗、亮的模式肯定其形状&#xff0c;而后用…...

CC++编译和链接介绍

介绍 C语言的编译和链接是将源代码转换为可执行文件的两个关键步骤。以下是详细的流程&#xff1a; 编译过程&#xff08;Compilation&#xff09; 预处理&#xff08;Preprocessing&#xff09;&#xff1a; 编译器首先对源代码进行预处理&#xff0c;这个阶段处理#include包…...

Element-UI中的el-upload插件上传文件action和headers参数

官网给的例子action都是绝对地址&#xff0c;我现在需要上传到自己后台的地址&#xff0c;只有一个路由地址/task/upload 根据 config/index.js配置&#xff0c;那么action要写成/api/task/upload&#xff0c;另外也可以传入函数来返回地址:action"uploadUrl()"。 …...

在IntelliJ IDEA中通过Spring Boot集成达梦数据库:从入门到精通

目录 博客前言 一.创建springboot项目 新建项目 选择创建类型​编辑 测试 二.集成达梦数据库 添加达梦数据库部分依赖 添加数据库驱动包 配置数据库连接信息 编写测试代码 验证连接是否成功 博客前言 随着数字化时代的到来&#xff0c;数据库在应用程序中的地位越来…...

docker相关

下载Ubuntu18.04文件64位&#xff08;32位安装不了MySQL&#xff09; https://old-releases.ubuntu.com/releases/18.04.4/?_ga2.44113060.1243545826.1617173008-2055924693.1608557140 Linux ubuntu16.04打开控制台&#xff1a;到桌面&#xff0c;可以按快捷键ctrlaltt 查…...

生产力工具|卸载并重装Anaconda3

一、Anaconda3卸载 &#xff08;一&#xff09;官方方案一&#xff08;Uninstall-Anaconda3-不能删除配置文件&#xff09; 官方推荐的方案是两种&#xff0c;一种是直接在Anaconda的安装路径下&#xff0c;双击&#xff1a; &#xff08;可以在搜索栏或者使用everything里面搜…...

大模型学习与实践笔记(十二)

使用RAG方式&#xff0c;构建opencv专业资料构建专业知识库&#xff0c;并搭建专业问答助手&#xff0c;并将模型部署到openxlab 平台 代码仓库&#xff1a;https://github.com/AllYoung/LLM4opencv 1&#xff1a;创建代码仓库 在 GitHub 中创建存放应用代码的仓库&#xff…...

Vulnhub靶机:FunBox 5

一、介绍 运行环境&#xff1a;Virtualbox 攻击机&#xff1a;kali&#xff08;10.0.2.15&#xff09; 靶机&#xff1a;FunBox 5&#xff08;10.0.2.30&#xff09; 目标&#xff1a;获取靶机root权限和flag 靶机下载地址&#xff1a;https://www.vulnhub.com/entry/funb…...

性能优化(CPU优化技术)-NEON指令介绍

「发表于知乎专栏《移动端算法优化》」 本文主要介绍了 NEON 指令相关的知识&#xff0c;首先通过讲解 arm 指令集的分类&#xff0c;NEON寄存器的类型&#xff0c;树立基本概念。然后进一步梳理了 NEON 汇编以及 intrinsics 指令的格式。最后结合指令的分类&#xff0c;使用例…...

【极数系列】Flink环境搭建(02)

【极数系列】Flink环境搭建&#xff08;02&#xff09; 引言 1.linux 直接在linux上使用jdk11flink1.18.0版本部署 2.docker 使用容器部署比较方便&#xff0c;一键启动停止&#xff0c;方便参数调整 3.windows 搭建Flink 1.18.0版本需要使用Cygwin或wsl工具模拟unix环境…...

仓储管理系统——软件工程报告(需求分析)②

需求分析 一、系统概况 仓库管理系统是一种基于互联网对实际仓库的管理平台&#xff0c;旨在提供一个方便、快捷、安全的存取货物和查询商品信息平台。该系统通过在线用户登录查询&#xff0c;可以线上操作线下具体出/入库操作、查询仓库商品信息、提高仓库运作效率&#xff…...

立创EDA学习:PCB布局

参考内容 【PCB布线教程 | 嘉立创EDA专业版入门教程&#xff08;11&#xff09;】 https://www.bilibili.com/video/BV1mW4y1Z7kb/?share_sourcecopy_web&vd_sourcebe33b1553b08cc7b94afdd6c8a50dc5a 单路布线 遵循顺序 先近后远&#xff0c;先易后难 可以拖动让拐角缩小…...

tomcat与Apache---一起学习吧之服务器

Apache和Tomcat都是Web服务器&#xff0c;但它们有一些重要的区别。 Apache服务器是普通服务器&#xff0c;本身只支持HTML即普通网页。不过可以通过插件支持PHP&#xff0c;还可以与Tomcat连通&#xff08;单向Apache连接Tomcat&#xff0c;就是说通过Apache可以访问Tomcat资…...

python打卡day49

知识点回顾&#xff1a; 通道注意力模块复习空间注意力模块CBAM的定义 作业&#xff1a;尝试对今天的模型检查参数数目&#xff0c;并用tensorboard查看训练过程 import torch import torch.nn as nn# 定义通道注意力 class ChannelAttention(nn.Module):def __init__(self,…...

Cloudflare 从 Nginx 到 Pingora:性能、效率与安全的全面升级

在互联网的快速发展中&#xff0c;高性能、高效率和高安全性的网络服务成为了各大互联网基础设施提供商的核心追求。Cloudflare 作为全球领先的互联网安全和基础设施公司&#xff0c;近期做出了一个重大技术决策&#xff1a;弃用长期使用的 Nginx&#xff0c;转而采用其内部开发…...

令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍

文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结&#xff1a; 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析&#xff1a; 实际业务去理解体会统一注…...

Web中间件--tomcat学习

Web中间件–tomcat Java虚拟机详解 什么是JAVA虚拟机 Java虚拟机是一个抽象的计算机&#xff0c;它可以执行Java字节码。Java虚拟机是Java平台的一部分&#xff0c;Java平台由Java语言、Java API和Java虚拟机组成。Java虚拟机的主要作用是将Java字节码转换为机器代码&#x…...

SQL Server 触发器调用存储过程实现发送 HTTP 请求

文章目录 需求分析解决第 1 步:前置条件,启用 OLE 自动化方式 1:使用 SQL 实现启用 OLE 自动化方式 2:Sql Server 2005启动OLE自动化方式 3:Sql Server 2008启动OLE自动化第 2 步:创建存储过程第 3 步:创建触发器扩展 - 如何调试?第 1 步:登录 SQL Server 2008第 2 步…...

沙箱虚拟化技术虚拟机容器之间的关系详解

问题 沙箱、虚拟化、容器三者分开一一介绍的话我知道他们各自都是什么东西&#xff0c;但是如果把三者放在一起&#xff0c;它们之间到底什么关系&#xff1f;又有什么联系呢&#xff1f;我不是很明白&#xff01;&#xff01;&#xff01; 就比如说&#xff1a; 沙箱&#…...

鸿蒙(HarmonyOS5)实现跳一跳小游戏

下面我将介绍如何使用鸿蒙的ArkUI框架&#xff0c;实现一个简单的跳一跳小游戏。 1. 项目结构 src/main/ets/ ├── MainAbility │ ├── pages │ │ ├── Index.ets // 主页面 │ │ └── GamePage.ets // 游戏页面 │ └── model │ …...

TJCTF 2025

还以为是天津的。这个比较容易&#xff0c;虽然绕了点弯&#xff0c;可还是把CP AK了&#xff0c;不过我会的别人也会&#xff0c;还是没啥名次。记录一下吧。 Crypto bacon-bits with open(flag.txt) as f: flag f.read().strip() with open(text.txt) as t: text t.read…...

CTF show 数学不及格

拿到题目先查一下壳&#xff0c;看一下信息 发现是一个ELF文件&#xff0c;64位的 ​ 用IDA Pro 64 打开这个文件 ​ 然后点击F5进行伪代码转换 可以看到有五个if判断&#xff0c;第一个argc ! 5这个判断并没有起太大作用&#xff0c;主要是下面四个if判断 ​ 根据题目…...

深入解析 ReentrantLock:原理、公平锁与非公平锁的较量

ReentrantLock 是 Java 中 java.util.concurrent.locks 包下的一个重要类,用于实现线程同步,支持可重入性,并且可以选择公平锁或非公平锁的实现方式。下面将详细介绍 ReentrantLock 的实现原理以及公平锁和非公平锁的区别。 ReentrantLock 实现原理 基本架构 ReentrantLo…...