当前位置: 首页 > 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资…...

工业安全零事故的智能守护者:一体化AI智能安防平台

前言&#xff1a; 通过AI视觉技术&#xff0c;为船厂提供全面的安全监控解决方案&#xff0c;涵盖交通违规检测、起重机轨道安全、非法入侵检测、盗窃防范、安全规范执行监控等多个方面&#xff0c;能够实现对应负责人反馈机制&#xff0c;并最终实现数据的统计报表。提升船厂…...

【WiFi帧结构】

文章目录 帧结构MAC头部管理帧 帧结构 Wi-Fi的帧分为三部分组成&#xff1a;MAC头部frame bodyFCS&#xff0c;其中MAC是固定格式的&#xff0c;frame body是可变长度。 MAC头部有frame control&#xff0c;duration&#xff0c;address1&#xff0c;address2&#xff0c;addre…...

以下是对华为 HarmonyOS NETX 5属性动画(ArkTS)文档的结构化整理,通过层级标题、表格和代码块提升可读性:

一、属性动画概述NETX 作用&#xff1a;实现组件通用属性的渐变过渡效果&#xff0c;提升用户体验。支持属性&#xff1a;width、height、backgroundColor、opacity、scale、rotate、translate等。注意事项&#xff1a; 布局类属性&#xff08;如宽高&#xff09;变化时&#…...

ssc377d修改flash分区大小

1、flash的分区默认分配16M、 / # df -h Filesystem Size Used Available Use% Mounted on /dev/root 1.9M 1.9M 0 100% / /dev/mtdblock4 3.0M...

深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法

深入浅出&#xff1a;JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中&#xff0c;随机数的生成看似简单&#xff0c;却隐藏着许多玄机。无论是生成密码、加密密钥&#xff0c;还是创建安全令牌&#xff0c;随机数的质量直接关系到系统的安全性。Jav…...

【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)

服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...

c++ 面试题(1)-----深度优先搜索(DFS)实现

操作系统&#xff1a;ubuntu22.04 IDE:Visual Studio Code 编程语言&#xff1a;C11 题目描述 地上有一个 m 行 n 列的方格&#xff0c;从坐标 [0,0] 起始。一个机器人可以从某一格移动到上下左右四个格子&#xff0c;但不能进入行坐标和列坐标的数位之和大于 k 的格子。 例…...

MODBUS TCP转CANopen 技术赋能高效协同作业

在现代工业自动化领域&#xff0c;MODBUS TCP和CANopen两种通讯协议因其稳定性和高效性被广泛应用于各种设备和系统中。而随着科技的不断进步&#xff0c;这两种通讯协议也正在被逐步融合&#xff0c;形成了一种新型的通讯方式——开疆智能MODBUS TCP转CANopen网关KJ-TCPC-CANP…...

新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案

随着新能源汽车的快速普及&#xff0c;充电桩作为核心配套设施&#xff0c;其安全性与可靠性备受关注。然而&#xff0c;在高温、高负荷运行环境下&#xff0c;充电桩的散热问题与消防安全隐患日益凸显&#xff0c;成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...

【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)

升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点&#xff0c;但无自动故障转移能力&#xff0c;Master宕机后需人工切换&#xff0c;期间消息可能无法读取。Slave仅存储数据&#xff0c;无法主动升级为Master响应请求&#xff…...