SpringBoot3(若依框架)集成Mybatis-Plus和单元测试功能,以及问题解决

一、Mybatis-Plus集成

  1. 新增依赖到父级pom.xml,原先的mybatis依赖可以不动
    在这里插入图片描述
    需要注意 mybatis-plus与mybatis版本之间的冲突,不要轻易改动依赖,不然分页也容易出现问题
    在这里插入图片描述
分类顶级pom.xml下面,如果没有引入还是出现报错,在common的模块下面再引入一份下面的依赖<!-- springboot3 / mybatis-plus 配置 --><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.16</version></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-spring-boot3-starter</artifactId><version>3.5.10</version></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-jsqlparser</artifactId><version>3.5.10</version></dependency>
  1. 替换原来的 MyBatis 配置,修改application.yml文件,修改mybatis配置为mybatis-plus
# MyBatis Plus配置
mybatis-plus:# 搜索指定包别名typeAliasesPackage: com.ruoyi.**.domain# 配置mapper的扫描,找到所有的mapper.xml映射文件mapperLocations: classpath*:mapper/**/*Mapper.xml# 加载全局的配置文件configLocation: classpath:mybatis/mybatis-config.xml
  1. 删除或者修改MyBatisConfig.java 配置 ,新增MybatisPlusConfig配置
@EnableTransactionManagement(proxyTargetClass = true)
@Configuration
public class MybatisPlusConfig {public static final ThreadLocal<String> TABLE_NAME = new ThreadLocal<>();@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();// 1. 动态表名配置 (兼容 MyBatis-Plus 3.5.5)DynamicTableNameInnerInterceptor dynamicTableNameInterceptor = new DynamicTableNameInnerInterceptor();// 创建表名处理器映射 (3.5.5 版本使用 setTableNameHandler 方法)Map<String, TableNameHandler> tableNameHandlerMap = new HashMap<>();BaseTableNameEnum.getAllBaseTableName().forEach(table ->tableNameHandlerMap.put(table, (sql, oldTable) ->Optional.ofNullable(TABLE_NAME.get()).orElse(oldTable)));// 3.5.5 版本设置表名处理器的方式dynamicTableNameInterceptor.setTableNameHandler((sql, tableName) -> {TableNameHandler handler = tableNameHandlerMap.get(tableName);return handler != null ? handler.dynamicTableName(sql, tableName) : tableName;});interceptor.addInnerInterceptor(dynamicTableNameInterceptor);// 2. 分页插件配置interceptor.addInnerInterceptor(paginationInnerInterceptor());// 3. 添加自定义的动态创建表拦截器interceptor.addInnerInterceptor(new DynamicCreateTableInterceptor());// 4. 攻击 SQL 阻断插件interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());return interceptor;}/*** 分页插件,自动识别数据库类型*/public PaginationInnerInterceptor paginationInnerInterceptor(){PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();// 设置数据库类型为mysqlpaginationInnerInterceptor.setDbType(DbType.MYSQL);// 设置最大单页限制数量,默认 500 条,-1 不受限制paginationInnerInterceptor.setMaxLimit(-1L);return paginationInnerInterceptor;}/*** 乐观锁插件*/public OptimisticLockerInnerInterceptor optimisticLockerInnerInterceptor(){return new OptimisticLockerInnerInterceptor();}/*** 如果是对全表的删除或更新操作,就会终止该操作*/public BlockAttackInnerInterceptor blockAttackInnerInterceptor(){return new BlockAttackInnerInterceptor();}
}

可以不用参考我的,因为我新增了拦截分表功能,官网有配置参考,可以参考官网
https://doc.ruoyi.vip/ruoyi/document/cjjc.html#%E9%9B%86%E6%88%90mybatis-plus%E5%AE%9E%E7%8E%B0mybatis%E5%A2%9E%E5%BC%BA

  1. 仅供参考的分表功能 MybatisPlusUtils 和 DynamicCreateTableInterceptor
/*** MybatisPlus工具类**/
public class MybatisPlusUtils {/*** 获取动态表名** @return*/public static String getDynamicTableName() {return MybatisPlusConfig.TABLE_NAME.get();}/*** 设置动态表名*/public static void setDynamicTableName(String tableName) {MybatisPlusConfig.TABLE_NAME.set(tableName);}/*** 清空当前线程设置的动态表名** @return* @author wk* @date 2022/2/9 10:37*/public static void emptyDynamicTableName() {MybatisPlusConfig.TABLE_NAME.remove();}/*** 获取基础表** @return*/public static String getBaseTableName(String dynamicTableName) {return BaseTableNameEnum.getBaseTableName(dynamicTableName);}
}
/*** 动态创建表拦截器**/
@Slf4j
public class DynamicCreateTableInterceptor implements InnerInterceptor {/*** 动态创建表** @param sh* @param connection* @param transactionTimeout*/@Overridepublic void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {String dynamicTableName = MybatisPlusUtils.getDynamicTableName();if (StringUtils.isBlank(dynamicTableName)) {return;}String baseTableName = MybatisPlusUtils.getBaseTableName(dynamicTableName);if (StringUtils.isNotBlank(baseTableName)&&!baseTableName.contains("null")) {try {String dataBase = connection.getCatalog();String sql = "SELECT count(1) FROM information_schema.tables WHERE table_schema=? AND table_name = ?";PreparedStatement preparedStatement = connection.prepareStatement(sql);preparedStatement.setString(1, dataBase);preparedStatement.setString(2, dynamicTableName);ResultSet resultSet = preparedStatement.executeQuery();if (resultSet.next()) {//获取表是否存在int count = resultSet.getInt(1);close(preparedStatement, resultSet);//如果表不存在if (count == 0) {sql = "SHOW CREATE TABLE " + baseTableName;//获取创建表语句preparedStatement = connection.prepareStatement(sql);resultSet = preparedStatement.executeQuery();if (resultSet.next()) {String createTableSql = resultSet.getString(2);close(preparedStatement, resultSet);//创建表sql = createTableSql.replaceFirst(baseTableName, dynamicTableName);preparedStatement = connection.prepareStatement(sql);preparedStatement.executeUpdate();close(preparedStatement, resultSet);log.info("【动态创建表成功】表名:{}", dynamicTableName);} else {close(preparedStatement, resultSet);}}} else {close(preparedStatement, resultSet);}} catch (Exception e) {log.info(String.format("【动态创建表失败】表名: %s", dynamicTableName), e);}}}/*** 关闭资源** @param preparedStatement* @param resultSet*/private void close(PreparedStatement preparedStatement, ResultSet resultSet) {if (preparedStatement != null) {try {preparedStatement.close();} catch (SQLException e) {e.printStackTrace();}}if (resultSet != null) {try {resultSet.close();} catch (SQLException e) {e.printStackTrace();}}}}
  1. 使用方式在需要增删改查的地方调用方法即可
 /*** 添加任务(使用动态表名)*/@Transactional@Overridepublic void addTaskWithDynamicTable(实体类 task) {task.setCreateTime(DateUtils.getNowDate());try {// 设置动态表名(按月份分表)String monthSuffix = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy"));String dynamicTable = "表名_" + monthSuffix;MybatisPlusUtils.setDynamicTableName(dynamicTable);// 插入数据(会自动使用动态表名)int result = 当前实现类的方法.insert(task);log.info("【动态表名插入】表名: {}, 结果: {}", dynamicTable, result);} finally {MybatisPlusUtils.emptyDynamicTableName();}}

二、集成单元测试

  1. 使用依赖,在ruoyi-admin的pom.xml下添加依赖
 <!-- 单元测试--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency>

不需要指定版本,自动依赖,下面是错误案例,在其他模块引入了单元测试版本不一样,造成混乱

       <!-- 单元测试-->
<!--        <dependency>-->
<!--            <groupId>org.springframework.boot</groupId>-->
<!--            <artifactId>spring-boot-test</artifactId>-->
<!--            <scope>test</scope>-->
<!--        </dependency>-->
<!--        <dependency>-->
<!--            <groupId>org.junit.jupiter</groupId>-->
<!--            <artifactId>junit-jupiter</artifactId>-->
<!--            <scope>test</scope>-->
<!--        </dependency>-->
<!--        <dependency>-->
<!--            <groupId>org.springframework</groupId>-->
<!--            <artifactId>spring-test</artifactId>-->
<!--            <scope>test</scope>-->
<!--        </dependency>--><!--        <dependency>-->
<!--            <groupId>junit</groupId>-->
<!--            <artifactId>junit</artifactId>-->
<!--            <version>4.13.2</version>-->
<!--        </dependency>-->
  1. 简简单单才是最好的,不然容易出现错误,一直以为引入的是JUnit 5结果是JUnit 4导致运行失败
    ,在ruoyi-admin的src下创建test模块(与main同级),导入依赖后更新maven,确保依赖加入
/**1. @description 测试MybatisPlus和分表功能*/

//JUnit 4
//@SpringBootTest(classes = Application.class,
//        webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
//@RunWith(SpringRunner.class)
//JUnit 5

("MybatisPlus测试")
public class MybatisPlusTest {//测试分页功能private service方法 taskMapper;private ISysUserService sysUserService;private RuoYiConfig ruoYiConfig;public void testContextLoad() {assertNotNull("taskService 注入失败", taskMapper);assertNotNull("sysUserService 注入失败", sysUserService);log.info("服务注入测试通过:"+ruoYiConfig.getName());}/*** 添加任务(使用动态表名)*/("添加任务(使用动态表名)")public void addTaskWithDynamicTable() {try {实体类 task = new  实体类();task.settName("张三");task.setAge("18");task.setPatientSex(1);task.setMobile("13888888888");task.setIdCard("420000000000000000");// 插入数据(会自动使用动态表名)taskMapper.insertHosCollectTaskInfo(task);} finally {
//            MybatisPlusUtils.emptyDynamicTableName();}}("测试查询功能")public void testContextLoads() {log.info("Spring上下文加载成功,taskMapper已注入");}("测试查询功能")public void testSelect() {log.info("测试查询功能");SysUser sysUser = sysUserService.selectUserById(1L);log.info("查询结果:{}", sysUser);}
}

三、问题解决

  1. 在 JUnit 5 中,不需要 @RunWith(SpringRunner.class),直接使用 @SpringBootTest 即可
  2. 在 JUnit 4 中,通常需要显式指定 @RunWith(SpringRunner.class),使用RunWith才能实例化到spring容器中
  3. JUnit 4如何没有引入 @RunWith,就会出现 NullPointerExecption,是因为 Spring 的依赖注入没有正确完成,或者相关的 Bean 没有被正确加载。
  4. 如果添加完成还是没有完成,还是服务注入失败问题,就需要使用 @ComponentScan 显式指定扫描包
// 正确配置启动类
(scanBasePackages = "com.ruoyi")
("com.ruoyi.**.mapper")
  1. 动态表名导致自动填充失效,实体类字段未正确配置自动填充策略
public void addTaskWithDynamicTable(实体类 task) {// 先设置动态表名MybatisPlusUtils.setDynamicTableName("表名");// 再手动设置时间(双重保障)task.setCreateTime(new Date());task.setUpdateTime(new Date());mapper类.insert(task);
}#避免措施:实体类字段使用正确注解
(fill = FieldFill.INSERT) 
private Date createTime;
  1. 分页插件冲突问题,PageHelper 与 MyBatis-Plus 分页不兼容
    原因:同时存在两套分页机制,最好是不要改动原来的依赖,然后引入新的mybatis-plus即可

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

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

相关文章

删除远程分支上非本分支的提交记录

要删除远程分支上非本分支的提交记录&#xff08;即主分支的提交历史&#xff09;&#xff0c;需要使用 Git 的重写历史功能。以下是完整解决方案&#xff1a; 解决方案步骤&#xff1a; 创建干净的新分支&#xff08;基于主分支最新提交&#xff09; # 切换到主分支并更新 git…

Flask input 和datalist结合

<input list"categories" name"category" id"category" class"form-control" placeholder"任务分类" required> 这段代码是一个 HTML 输入控件&#xff0c;结合了 <input> 和 <datalist>&#xff0c;用来…

嵌入式分享#27:原来GT911有两个I2C地址(全志T527)

最近在调试全志T527的触摸功能时&#xff0c;发现GT911触摸芯片的I2C地址有时是0x5d&#xff0c;有时又识别成0x14&#xff0c;不知道大家有没有遇到过类似这个情况。虽然最后使用0x5d地址调通了触摸功能&#xff0c;但是一直还是很困惑&#xff0c;为什么会出现0x14和0x5d两个…

Linux运维新人自用笔记(Rsync远程传输备份,服务端、邮箱和客户端配置、脚本)

内容全为个人理解和自查资料梳理&#xff0c;欢迎各位大神指点&#xff01;每天学习较为零散。day24一、Rsync传输文件#安装rsync#-a递归同步&#xff08;包含子目录&#xff09;保留文件权限、所有者、组、时间戳等元数据 #​​-z传输时压缩数据 #​​-v显示详细同步过程 #​​…

以 “有机” 重构增长:云集从电商平台到健康生活社区的跃迁

当电商行业陷入流量争夺的红海&#xff0c;同质化运营模式难以突破增长瓶颈时&#xff0c;云集以从精选电商到有机生活平台的战略转型&#xff0c;开辟出差异化发展路径。其转型并非凭经验决断的孤例&#xff0c;而是建立在对市场趋势的精准研判、用户需求的深度解码&#xff0…

【2025最新版】midjourney小白零基础入门到精通教程!人工智能绘图+AI绘图+AI画图,一键出图教程 (持续更新)

前言 现在市面上相关的AI绘画工具非常多&#xff0c;有6pen.art、Stable Diffusion、DALL.E、Midjourney等。 而MJ就目前而言&#xff0c;它是一款强大的人工智能工具&#xff0c;旨在帮助设计师和创意人员完成各种设计任务。 非常适合我们图像工作者&#xff0c;从 UI 设计到…

2025年渗透测试面试题总结-2025年HW(护网面试) 70(题目+回答)

安全领域各种资源&#xff0c;学习文档&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具&#xff0c;欢迎关注。 目录 2025年HW(护网面试) 70 一、自我介绍 二、同源策略 & 三大漏洞对比解析 1. 同源策略&#xff08;SOP&…

加权卡尔曼滤波

加权卡尔曼滤波融合&#xff0c;它通过给不同传感器或估计结果分配不同的权重&#xff0c;来提高状态估计的精度和可靠性。一、卡尔曼滤波1.状态方程2.观测方程其中&#xff1a;基本方程①状态一步预测②状态估计③滤波增益④一步预测均方差⑤估计均方误差二、加权卡尔曼滤波对…

【世纪龙科技】新能源汽车维护与故障诊断-汽车专业数字课程资源

在职业院校汽车专业教学中&#xff0c;理论与实践脱节、设备投入不足、学生实操能力薄弱等问题长期存在。如何让学生在有限的教学资源下掌握新能源汽车核心技术&#xff1f;如何让教师更高效地开展理实一体化教学&#xff1f;《新能源汽车维护与故障诊断》数字课程资源&#xf…

Windows Server系统安装JDK,一直卡在“应用程序正在为首次使用作准备,请稍候”

一、背景 第二次遇到这个问题了&#xff0c;但是居然没想起来之前遇到过&#xff0c;又问元宝给的答案不对&#xff0c;还没想起来之前收藏过解决方案&#xff0c;这里特别记录一下。 二、问题描述 操作系统是Windows Sever2019&#xff0c;安装JDK时卡住一直过不去&#xff0…

机器学习入门:线性回归详解与实战

线性回归&#xff08;Linear Regression&#xff09;是机器学习中最基础也最常用的算法之一&#xff0c;无论是初学者入门还是实际业务场景&#xff0c;都能看到它的身影。本文将从概念、原理到代码实现&#xff0c;带你全方位了解线性回归。一、什么是线性回归&#xff1f;简单…

第3篇:软链接 mklink /D 教程:轻量缓存目录迁移利器

我们通过诸多实践后将三种链接方案分别独立成篇&#xff0c;可以让不同需求场景的读者精准获取所需内容。下面是回顾我们文章系列策划的三篇博客标题、定位和详细大纲&#xff0c;每篇都围绕一个核心方案展开&#xff0c;具备教学性、实用性和实操性&#xff1a; &#x1f4d8;…

力扣 hot100 Day52

124. 二叉树中的最大路径和 二叉树中的 路径 被定义为一条节点序列&#xff0c;序列中每对相邻节点之间都存在一条边。同一个节点在一条路径序列中 至多出现一次 。该路径 至少包含一个 节点&#xff0c;且不一定经过根节点。 路径和 是路径中各节点值的总和。 给你一个二叉…

数据存储:OLAP vs OLTP

下面系统性地进行介绍,包括OLAP数据库的基本概念、特点、常见产品,以及它们在实际工作中的典型应用场景,最后对比与关系型数据库(OLTP)的区别。 一、OLAP数据库是什么? OLAP(Online Analytical Processing,联机分析处理)数据库,主要用于大数据量、多维度、复杂查询与…

云原生网络策略自动化在微服务架构 API 安全防护与流量管理中的应用

云原生网络策略自动化在微服务架构中的核心价值随着微服务架构在金融、电商等领域的广泛应用&#xff0c;API安全防护与流量管理已成为企业数字化转型的关键挑战。Gartner 2023年报告显示&#xff0c;83%的分布式系统因网络策略缺失导致安全事件&#xff0c;而传统静态策略配置…

无需云服务器的内网穿透方案 -- cloudflare tunnel

内网穿透 原文地址 https://docs.caolib.dpdns.org/network/cloudflare tunnel.html Cloudflare Tunnel 内网穿透工具 1.简介 1.1 介绍 官方介绍&#xff1a;Cloudflare Tunnel 为您提供了一种安全的方式&#xff0c;无需公开路由的 IP 地址即可将资源连接到 Cloudflare。使用…

目前市面上arm64-v8a、armeabi-v7a设备的市占率有多少?为什么x86架构的手机越来越少?

deepseek回答&#xff1a; 当前全球范围内&#xff0c;arm64-v8a 架构在安卓设备中的市占率已超过 64%&#xff0c;远超其他架构版本。具体分布如下&#xff1a; &#x1f4ca; 各架构市场份额对比 架构类型市占率定位与趋势arm64-v8a≥64%主流 64 位架构&#xff0c;性能最…

Java中配置两个r2db连接不同的数据库

Java中配置两个r2db连接不同的数据库在实际项目中不可避免的存在使用两个数据库的情况&#xff0c;下面将系统地讲解相关配置方案&#xff0c;包含配置文件、数据库配置类、注解原理、常见错误排查等维度&#x1f9e9; 一、配置文件说明&#xff08;application.yml&#xff09…

Swagger 配置及使用指南

Spring Boot 项目集成 Swagger 配置及使用指南 一、Swagger 简介 Swagger 是一个用于设计、构建、文档化和使用 RESTful API 的框架。通过集成 Swagger&#xff0c;开发者可以&#xff1a; 自动生成实时 API 文档直接在浏览器中测试 API 接口减少手动编写文档的工作量支持团队协…

什么是缓存雪崩?缓存击穿?缓存穿透?分别如何解决?什么是缓存预热?

缓存雪崩&#xff1a;在一个时间段内&#xff0c;有大量的key过期&#xff0c;或者Redis服务宕机&#xff0c;导致大量的请求到达数据库,带来巨大压力- 给key设置不同的TTL、利用Redis集群提高服务的高可用性、添加多级缓存、添加降级流策略缓存击穿&#xff1a;给某一个key设置…