深入剖析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的独特作用与诞生背景
-
简化CRUD操作
BaseMapper已经提供了基础的增删改查功能,然而,在实际业务开发中,我们可能会遇到更复杂的数据处理场景,比如批量插入更新、组合查询、分组统计等。BaseMapperPlus正是为了满足这些高级需求而设计,通过提供一系列便捷的方法,简化了数据操作层面上的代码编写。 -
定制化方法
开发者可以根据项目特点,在BaseMapperPlus中添加一些通用的、适用于大部分实体类的方法,如根据多个字段联合查询、特殊条件下的删除操作等。这样,每个具体业务对象对应的mapper接口只需继承BaseMapperPlus就能获得丰富的预定义功能。 -
抽象复用
通过创建BaseMapperPlus接口,可以将常见的数据库操作逻辑抽象出来,实现代码复用,降低重复开发成本,同时保持代码的整洁性和一致性。
二、继承BaseMapperPlus的好处
-
减少重复代码
继承BaseMapperPlus后,可以直接调用其中预先定义好的方法,避免了在每个具体的mapper接口中手动编写相似的操作逻辑。 -
提升开发效率
开发者无需关心底层SQL细节,仅需关注业务逻辑本身,能够更快地完成数据访问层的开发工作。 -
易于维护
所有通用的数据库操作逻辑都被集中到了BaseMapperPlus中,一旦需要修改或优化,只需要在一个地方改动即可,有利于后期的维护和升级。
三、BaseMapperPlus示例方法及优势
虽然BaseMapperPlus的具体内容取决于项目的实际需求,但它通常会包含以下类型的方法:
- 批量操作增强:如批量插入、批量更新、批量删除。
- 条件查询增强:支持更复杂的组合查询、排序方式自定义、结果集过滤等功能。
- 统计分析增强:提供分组统计、聚合函数使用、分页统计等功能。
- 事务控制:如果框架本身不提供,则可以在
BaseMapperPlus中加入对事务管理的支持。
相比原生的MyBatis-Plus,BaseMapperPlus的优势在于它更加贴近项目实际,具有更强的针对性和灵活性,能够解决更多个性化的需求,从而让开发者能专注于业务逻辑的实现,而不是底层数据操作的繁琐编码。
总结
尽管BaseMapperPlus不是MyBatis-Plus的标准组件,但在实际开发过程中,这样的扩展接口体现了极高的实用价值。它以一种可复用、易维护的方式增强了MyBatis-Plus的功能,使得开发者在面对各种复杂数据库操作时,能享受到更高程度的便利性。在构建项目时,建议结合具体业务需求来设计并实现一个符合自己团队习惯的BaseMapperPlus接口,从而最大化地发挥出它的价值。
相关文章:
深入剖析BaseMapperPlus扩展接口及其在MyBatis-Plus中的实践价值
前言 BaseMapperPlus并非MyBatis-Plus(MP)官方提供的标准接口,而是社区开发者基于MP的BaseMapper接口进行二次封装和增强后创建的一个自定义接口。这个概念可能因不同项目或个人实践而有所差异,但其核心思想是为了解决特定场景下…...
Linux之安装配置VCentOS7+换源
目录 一、安装 二、配置 三、安装工具XSHELL 3.1 使用XSHELL连接Linux 四、换源 前言 首先需要安装VMware虚拟机,在虚拟机里进行安装Linux 简介 Linux,一般指GNU/Linux(单独的Linux内核并不可直接使用,一般搭配GNU套件&#…...
[极客大挑战 2019]LoveSQL1
万能密码测试,发现注入点 注意这里#要使用url编码才能正常注入 测试列数,得三列 查看table,一个是geekuser另一个是l0ve1ysq1 查看column,有id,username,password,全部打印出来,…...
网络安全的介绍
1.什么是网络安全 网络安全是一门关注保护计算机系统、网络基础设施和数据免受未经授权访问、破坏或窃取的学科。随着数字化时代的发展,网络安全变得尤为重要,因为大量的个人信息、商业机密和政府数据都储存在电子设备和云端系统中。以下是网络安全的概…...
django邮件通知功能-
需求: 1:下单人员下订单时需要向组长和投流手发送邮件通知 2:为何使用邮件通知功能?因为没钱去开通短信通知功能 设计 1:给用户信息表添加2个字段 第一个字段为:是否开通邮件通知的布尔值 第二个字段为: 用…...
C++ 类定义
C 类定义 定义一个类需要使用关键字 class,然后指定类的名称,并类的主体是包含在一对花括号中,主体包含类的成员变量和成员函数。 定义一个类,本质上是定义一个数据类型的蓝图,它定义了类的对象包括了什么࿰…...
IntelliJ IDE 插件开发 | (五)VFS 与编辑器
系列文章 IntelliJ IDE 插件开发 |(一)快速入门IntelliJ IDE 插件开发 |(二)UI 界面与数据持久化IntelliJ IDE 插件开发 |(三)消息通知与事件监听IntelliJ IDE 插件开发 |(四)来查收…...
金融OCR领域实习日志(一)
一、OCR基础 任务要求: 工作原理 OCR(Optical Character Recognition,光学字符识别)是指电子设备(例如扫描仪或数码相)检查纸上打印的字符,经过检测暗、亮的模式肯定其形状,而后用…...
CC++编译和链接介绍
介绍 C语言的编译和链接是将源代码转换为可执行文件的两个关键步骤。以下是详细的流程: 编译过程(Compilation) 预处理(Preprocessing): 编译器首先对源代码进行预处理,这个阶段处理#include包…...
Element-UI中的el-upload插件上传文件action和headers参数
官网给的例子action都是绝对地址,我现在需要上传到自己后台的地址,只有一个路由地址/task/upload 根据 config/index.js配置,那么action要写成/api/task/upload,另外也可以传入函数来返回地址:action"uploadUrl()"。 …...
在IntelliJ IDEA中通过Spring Boot集成达梦数据库:从入门到精通
目录 博客前言 一.创建springboot项目 新建项目 选择创建类型编辑 测试 二.集成达梦数据库 添加达梦数据库部分依赖 添加数据库驱动包 配置数据库连接信息 编写测试代码 验证连接是否成功 博客前言 随着数字化时代的到来,数据库在应用程序中的地位越来…...
docker相关
下载Ubuntu18.04文件64位(32位安装不了MySQL) https://old-releases.ubuntu.com/releases/18.04.4/?_ga2.44113060.1243545826.1617173008-2055924693.1608557140 Linux ubuntu16.04打开控制台:到桌面,可以按快捷键ctrlaltt 查…...
生产力工具|卸载并重装Anaconda3
一、Anaconda3卸载 (一)官方方案一(Uninstall-Anaconda3-不能删除配置文件) 官方推荐的方案是两种,一种是直接在Anaconda的安装路径下,双击: (可以在搜索栏或者使用everything里面搜…...
大模型学习与实践笔记(十二)
使用RAG方式,构建opencv专业资料构建专业知识库,并搭建专业问答助手,并将模型部署到openxlab 平台 代码仓库:https://github.com/AllYoung/LLM4opencv 1:创建代码仓库 在 GitHub 中创建存放应用代码的仓库ÿ…...
Vulnhub靶机:FunBox 5
一、介绍 运行环境:Virtualbox 攻击机:kali(10.0.2.15) 靶机:FunBox 5(10.0.2.30) 目标:获取靶机root权限和flag 靶机下载地址:https://www.vulnhub.com/entry/funb…...
性能优化(CPU优化技术)-NEON指令介绍
「发表于知乎专栏《移动端算法优化》」 本文主要介绍了 NEON 指令相关的知识,首先通过讲解 arm 指令集的分类,NEON寄存器的类型,树立基本概念。然后进一步梳理了 NEON 汇编以及 intrinsics 指令的格式。最后结合指令的分类,使用例…...
【极数系列】Flink环境搭建(02)
【极数系列】Flink环境搭建(02) 引言 1.linux 直接在linux上使用jdk11flink1.18.0版本部署 2.docker 使用容器部署比较方便,一键启动停止,方便参数调整 3.windows 搭建Flink 1.18.0版本需要使用Cygwin或wsl工具模拟unix环境…...
仓储管理系统——软件工程报告(需求分析)②
需求分析 一、系统概况 仓库管理系统是一种基于互联网对实际仓库的管理平台,旨在提供一个方便、快捷、安全的存取货物和查询商品信息平台。该系统通过在线用户登录查询,可以线上操作线下具体出/入库操作、查询仓库商品信息、提高仓库运作效率ÿ…...
立创EDA学习:PCB布局
参考内容 【PCB布线教程 | 嘉立创EDA专业版入门教程(11)】 https://www.bilibili.com/video/BV1mW4y1Z7kb/?share_sourcecopy_web&vd_sourcebe33b1553b08cc7b94afdd6c8a50dc5a 单路布线 遵循顺序 先近后远,先易后难 可以拖动让拐角缩小…...
tomcat与Apache---一起学习吧之服务器
Apache和Tomcat都是Web服务器,但它们有一些重要的区别。 Apache服务器是普通服务器,本身只支持HTML即普通网页。不过可以通过插件支持PHP,还可以与Tomcat连通(单向Apache连接Tomcat,就是说通过Apache可以访问Tomcat资…...
【仅限首批内测用户知晓】:Midjourney v7隐藏参数、语义理解跃迁与提示词重构法则
更多请点击: https://intelliparadigm.com 第一章:Midjourney v7核心架构演进与内测准入机制 Midjourney v7 采用全新异构推理引擎(Heterogeneous Inference Engine, HIE),将扩散主干网络、语义对齐模块与多模态提示解…...
初识Verilog
...
3步掌握微信聊天记录导出:永久保存你的数字记忆
3步掌握微信聊天记录导出:永久保存你的数字记忆 【免费下载链接】WeChatExporter 一个可以快速导出、查看你的微信聊天记录的工具 项目地址: https://gitcode.com/gh_mirrors/wec/WeChatExporter 你是否担心手机丢失或更换时,珍贵的微信聊天记录会…...
构建多模型容灾策略Taotoken的路由能力实战解析
🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 构建多模型容灾策略:Taotoken的路由能力实战解析 应用场景类,针对对服务稳定性要求高的企业级应用…...
OpenClaw QQ机器人一键接入指南
准备工作 软件环境 已成功安装并运行 OpenClaw Windows 版本OpenClaw Gateway 运行状态正常(建议保持在线状态) 账号准备 已准备好有效的 QQ 账号(用于平台扫码登录)已安装 QQ 手机客户端(用于扫码登录及机器人功…...
《AUTOSAR软件组件(SWC)实战:基于ETAS工具链的接口与数据映射》
1. AUTOSAR软件组件(SWC)基础概念 在汽车电子开发领域,AUTOSAR(汽车开放系统架构)已经成为行业标准。软件组件(SWC)作为AUTOSAR架构中的核心元素,承担着实现具体功能的重任。简单来说,SWC就像乐高积木,每个…...
ClickHouse:开源数据引擎在AI浪潮爆发,挑战传统数据库巨头
ClickHouse:开源数据引擎爆发,在AI浪潮中挑战传统数据库巨头过去18个月,开源数据基础设施里最热的公司除了Supabase可能就是ClickHouse了。ClickHouse Cloud ARR在2025年保持250%的同比增速,第三方估计从2024年中的约1500万美元增…...
使用Hermes Agent框架时接入Taotoken自定义供应商的步骤详解
🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 使用Hermes Agent框架时接入Taotoken自定义供应商的步骤详解 对于使用Hermes Agent框架的开发者而言,将后端AI服务接入…...
C#架构师实战:构建确定性事件驱动系统的工程原则与技术栈
1. 从个人简介到架构哲学:一位资深C#架构师的工程实践全景看到这个标题,你可能会以为这是一个普通的GitHub个人主页介绍。但如果你是一位深耕于分布式系统、事件驱动架构,或者正在为构建高确定性、可观测的生产级系统而头疼的工程师ÿ…...
终极解决方案:3分钟搞定百度网盘提取码的免费自动化工具
终极解决方案:3分钟搞定百度网盘提取码的免费自动化工具 【免费下载链接】baidupankey 项目地址: https://gitcode.com/gh_mirrors/ba/baidupankey 还在为百度网盘资源下载卡在提取码这一步而烦恼吗?每次遇到需要密码的分享链接,都要…...
