资讯动态

Java 项目实战: 交友项目 - MongoDB基础总结与自检清单

发布时间:2026/10/23 10:34:10 来源:尧图企业网站定制
概述我们要把项目从「MySQL 单存储」升级成了「MySQL MongoDB 双存储」通用设置和黑名单继续走mf-dubbo-db今日佳人把缘分值数据首次存进了 Mongomf-dubbo-mongo模块从零搭起来并跑通了第一条 Dubbo 链路。这里不重复细节只做三件事把整章串成一张知识地图、沉淀可复用的套路清单、给出可勾选的自检项。纲要本章复盘四个功能块各自解决了什么问题对应已交付的哪些篇目知识地图从 MySQL 域到 Mongo 域的完整链路Mermaid可复用套路清单新存储接入三步、MyBatis-Plus ↔ MongoTemplate 心智迁移、跨存储组装、条件下沉、insert vs save关键坑回顾表全章最容易翻车的十来个点自检清单每项都给出「怎么验」练习建议四个可验证的技术任务含作业改造常见误解澄清 下一章预告本章做了什么四块内容的复盘按讲义的推进顺序本章四个功能块和已交付篇目的对应关系功能块存储归属解决的问题对应篇目通用设置通知开关 陌生人问题MySQL单表 CRUD 的「存在则更新、不存在则保存」二段式写法MongoDB引入前置黑名单分页 移除MySQL子查询跨表分页tb_user_info where id in (select ...)MyBatis-Plus 自定义分页MongoDB引入前置MongoDB 概念 命令行Mongo 本身体系结构、BSON、insert/update/remove/find、索引与 explainMongoDB介绍MongoDB的体系结构MongDB的入门命令行操作SpringData-Mongo 今日佳人Mongo MySQL 组装环境搭建、MongoTemplate CRUD、queryWithMaxScore、跨存储组装 VOSpringData-Mongo环境搭建SpringData-MongoCRUD操作上/下今日佳人三篇、推荐好友列表需求分析本章结束时工程的形态实读01-代码/mf快照mf ├── mf-app-server # 应用层SettingsController / mfService跨存储组装 ├── mf-model │ └── src/main/java/com/mf/model │ ├── domain # MySQL 实体Settings / Question / BlackList / UserInfo │ ├── mongo │ │ └── RecommendUser.java # 本章新增Document(collection recommend_user) │ └── vo # SettingsVo / PageResult / TodayBest └── mf-dubbo ├── mf-dubbo-interface # 契约SettingsApi / QuestionApi / BlackListApi / RecommendUserApi ├── mf-dubbo-db # MySQL 实现三套 ApiImpl Mapper端口不变 └── mf-dubbo-mongo # 本章新增RecommendUserApiImpl端口 18082dubbo 20882有一个贯穿全章的结构性变化值得单独记一笔mf-model引入spring-boot-starter-data-mongodb之后所有依赖它的模块都具备了 Mongo 自动装配能力mf-dubbo-db和mf-app-server明明不连 Mongo启动时却会去找localhost:27017然后报错。解法是这两个模块的引导类排除MongoAutoConfiguration和MongoDataAutoConfiguration。这个「依赖传递引入了不想要的自动配置」是 Spring Boot 项目的经典问题记住现象和排除写法即可下章还会遇到类似的事。本章知识地图应用层组装mf-app-serverMongoDB 域MySQL 域mf-dubbo-dbDubbo: queryWithMaxScoreDubboDubbo缓存层通用设置SettingsApi / QuestionApi黑名单分页BlackListApi 子查询概念与体系结构document / collection / BSON命令行insert / update / find / 索引SpringData-MongoMongoTemplate Document今日佳人 /mf/todayBestMongo 查缘分 MySQL 查资料推荐好友列表分页批查 兜底mf-dubbo-mongoRecommendUserApiImplRedis读这张图的角度左上是存量能力MySQL 域中间是本章新练的内功概念 → 命令行 → SpringData-Mongo右下是内功第一次落地mf-dubbo-mongo模块 跨存储组装。下一章的圈子功能就是把中间这条 Mongo 链路全面铺开。可复用套路清单这一节是全章的沉淀五个套路在后续章节反复出现值得逐条过一遍。套路一新存储接入三步本章接入 MongoDB 的过程可以抽象成固定三步以后接入任何新存储Redis、ES、OSS都是这个节奏步骤本章落地对应的 MySQL 侧完全对称依赖 连接配置mf-dubbo-mongo的 pom 加 starteryml 配spring.data.mongodb.uri端口 18082mf-dubbo-db的 datasource 配置实体注解映射RecommendUser标Document(collection recommend_user)、Id实体标TableName、TableId独立域服务模块mf-dubbo-mongo放DubboService实现包扫描com.mf.dubbo.apimf-dubbo-db同构第三步的关键词是存储可插拔接口契约放在mf-dubbo-interface实现放在哪个模块消费方完全不感知。哪天把黑名单从 MySQL 迁到 Mongo练习建议里有这个任务mf-app-server一行代码都不用改。套路二MyBatis-Plus ↔ MongoTemplate 心智迁移表写过 MyBatis-Plus 的人上手 MongoTemplate真正要做的只是「翻译」操作意图MyBatis-Plus 写法MongoTemplate 写法实体→表映射TableName(tb_settings)Document(collection recommend_user)主键TableIdLong自增IdObjectId12 字节ObjectId.get()生成字段改名TableField(user_id)Field(username)等值条件qw.eq(user_id, userId)Criteria.where(toUserId).is(toUserId)条件容器QueryWrapperQuery.query(criteria)排序qw.orderByDesc(score).with(Sort.by(Sort.Order.desc(score)))分页Page对象 分页插件手动count().skip((page-1)*size).limit(size)查一条selectOne(qw)findOne(query, Class)新增insert(entity)insert(entity)或save(entity)语义有差别见套路五删除mapper.delete(qw)remove(query, Class)分页这一行差异最大MyBatis-Plus 有分页插件兜底Mongo 侧的 skip/limit 和 count 是自己拼的参数换算错一个符号就翻车坑表里有。套路三跨存储组装模式Mongo 存关系数据缘分值、后续的点赞评论MySQL 存展示资料昵称头像应用层组装。本章落地了三次形态略有不同场景Mongo 侧查询MySQL 侧查询组装要点今日佳人queryWithMaxScore单对象score 倒序取第一条userInfoApi.findById单查冷启动兜底查不到就伪造userId1, score99黑名单分页—全在 MySQL子查询分页一次查齐条件下沉到 SQL不在 Java 层过滤推荐好友列表按 toUserId 查一批 RecommendUser按用户 id 批量查 UserInfo批查防 N1先收集 id 再findByIds不要循环单查缺失兜底防 NPE两次跨存储调用之间没有事务保证这是这个模式的固有代价所以 Mongo 里只放「价值较低、可再生成」的关系数据核心账户资料仍然留在 MySQL。这个存储分工原则比代码本身更值得记住。套路四条件下沉 SQL跨存储、跨表的过滤条件必须下沉到查询层SQL 的 where 子查询、Mongo 的 Criteria不能查回来在 Java 里filter。黑名单分页是最典型的反例教材如果先查tb_black_list全量再在 Java 里按页切割counts总数和pages总页数就算不对了前端分页控件直接失真。过滤逻辑和分页统计必须发生在同一层。套路五insert vs save 语义mongoTemplate的两个保存方法长得像语义不同方法已存在相同_id时典型场景insert抛DuplicateKeyException新增语义主键由系统生成save整文档覆盖相当于 upsert保存语义不在乎是新增还是更新MySQL 侧对应的「存在则更新、不存在则保存」二段式saveQuestion里先findByUserId再分支在 Mongo 侧可以用save一步替代但要清楚代价save 是整文档替换字段会被清掉和 update 语句漏写$set是同一种事故见坑表第一行。关键坑回顾表全章最容易翻车的点按出现顺序回顾坑现象正确姿势update 漏写$setdb.user.update({id:1},{age:25})把整个文档替换成只剩 age部分更新必须{$set:{...}}Java 侧同理save会整文档覆盖remove({})空条件删光整个集合无确认弹窗删除必带条件清空集合用drop()并想清楚字段名写错大小写Criteria 里写touserid静默查不到不报错Mongo 无 schema 校验字段名以插入时为准排查手法先find()看原始文档skip/limit 换算错误第 2 页查出了第 1 页的数据skip((page-1)*size)注意 page 从 1 开始userId/toUserId 写反查「我的今日佳人」查出别人的缘分值约定toUserId是「看的人」userId是「被推荐的人」recommend_user 缺唯一索引同一对用户重复插入推荐记录这个坑黑名单场景已经是第三次出现了业务唯一键建唯一索引db.recommend_user.createIndex({userId:1,toUserId:1},{unique:true})兜底数据 NPE冷启动伪造了 RecommendUser但userInfoApi.findById(1L)查不到 id1 的资料兜底要兜两层推荐数据兜了资料也要有真实可用的用户Double 截断score99.6经longValue()变 99前端显示少 0.6 缘分值明确取整策略longValue()是截断不是四舍五入需要精度就Math.roundJava 层过滤分页 total 与实际条数对不上过滤条件下沉到 SQL/Criteria套路四分页插件未配置selectPage静默查全量数据小的时候根本发现不了mf-dubbo-db引导类注册PaginationInnerInterceptorMongo 自动装配误伤mf-dubbo-db/mf-app-server启动报连接localhost:27017失败引导类exclude {MongoAutoConfiguration.class, MongoDataAutoConfiguration.class}自检清单部署或复习完本章逐项勾掉检查项怎么验Mongo 容器能连docker ps看到 27017 端口映射shell 里show dbs有响应mf-dubbo-mongo注册成功Nacos 控制台服务列表出现mf-dubbo-mongo端口 18082实体映射正确shell 执行db.recommend_user.find().pretty()字段名与RecommendUser的属性一致驼峰不是下划线CRUD 走通测试类依次跑 save / find / update / remove每步用find()核对结果今日佳人返回缘分值带 token 请求GET /mf/todayBest响应里有fateValue且非 0冷启动兜底生效清空recommend_user集合再请求接口不报错返回 userId1 的默认资料和缘分值 99通用设置二段式保存同一用户连续两次设置陌生人问题tb_question里始终只有一条记录黑名单分页正确拉黑 3 人后请求GET /settings/blacklist?page1size2counts3、pages2练习建议原课程的作业偏「照流程再走一遍」这里改写成四个可验证的技术任务难度递增任务一给 recommend_user 的 toUserId 建索引queryWithMaxScore每次首页刷新都会执行但recommend_user目前只有_id索引按toUserId查必然全集合扫描。练习内容// 建(toUserId, score)复合索引查询条件与排序一次覆盖db.recommend_user.createIndex({toUserId:1,score:-1})验证建索引前后分别执行db.recommend_user.find({toUserId: 1002}).sort({score: -1}).explain()对比winningPlan.stage从COLLSCAN变为IXSCAN。顺手灌 10 万条测试数据for循环插入命令行篇章有模板感受数量级差异。任务二todayBest 加 Redis 缓存今日佳人是首页首个请求每次都跨两次 Dubbo 调用不值当。设计要点key 用today_best::{userId}value 存TodayBest序列化结果过期时间 5~10 分钟。要思考的问题是推荐数据更新时缓存怎么办先做「短过期时间自然淘汰」这个最简方案把一致性方案的取舍留成注释。任务三黑名单改 Mongo 实现新建black_list集合BlackListApi契约不动在mf-dubbo-mongo里写一套新实现。做完对比MySQL 版靠一条子查询 SQL 搞定分页Mongo 版不支持 join得先分页查black_list、再批查user_info组装多了一次跨存储往返换来了关系数据迁出主库。这个取舍没有标准答案写清楚你选哪个、为什么。任务四兜底数据配置化降级mfService.todayBest()里硬编码的userId1L, score99d有两个问题id1 的用户可能不存在NPE 风险换兜底用户要改代码重新发版。改造方向抽成配置项ConfigurationProperties或放 Nacos再考虑兜底用户池随机取一个真实存在的「官方账号」。验收标准把配置里的 userId 改成一个不存在的值服务不崩、接口返回明确降级标记。原作业里的「修改手机号」功能本质上是一次登录链路复用练习发短信 → 校验验证码 → 保存手机号三步分别对应登录功能已写过的组件适合作为动手热身此处不再展开。常见误解澄清误解事实「引入 MongoDB 是为了替代 MySQL」两者分工MySQL 存核心账户资料Mongo 存海量、低价值密度的关系数据推荐、后续的动态点赞评论Redis 做缓存。选型依据是数据特点不是新旧「Mongo 没有 schema所以不用设计字段」恰恰相反没有 schema 校验意味着写错字段名不报错、静默查不到。字段命名约定统一驼峰要靠代码纪律保证「Mongo 不支持索引和执行计划」createIndex/getIndexes/explain全套都有IXSCAN与COLLSCAN的区别和 MySQL 的索引扫描/全表扫描一一对应「save就是更新insert就是新增」save是「存在则整文档覆盖不存在则插入」两个方法都能新增差别在_id冲突时的行为「实体类加Document只影响 Mongo 模块」依赖在mf-model所有引用它的模块都会触发 Mongo 自动装配不连 Mongo 的模块必须显式 exclude「分页插件配一次所有模块都生效」插件是mf-dubbo-db引导类里的Bean只在配置了的模块生效新模块要分页得重新注册下一章预告第四章进入圈子功能发布动态、浏览好友动态、点赞、评论。这一章的价值密度很高动态、评论、点赞全面使用 MongoDB存储本章练的Document映射、MongoTemplate 查询、mf-dubbo-mongo模块扩展会一次性铺开。工程实读也能佐证本章mf-dubbo-mongo的快照里已经出现了UserLike相关的 import说明圈子部分的代码与推荐模块同源生长。套路三的跨存储组装在圈子场景会升级成「动态Mongo 用户资料MySQL 点赞评论状态Mongo」的三方组装批查防 N1 会从建议变成刚需。总结本章的工程成果是双存储格局落地mf-dubbo-db通用设置/黑名单与mf-dubbo-mongo今日佳人/推荐并列接口契约统一放mf-dubbo-interface消费方对存储无感知最值得带走的不是 MongoTemplate 的 API而是五个套路新存储接入三步、MyBatis-Plus 心智迁移、跨存储组装批查 兜底、条件下沉、insert vs save 语义坑的共性也值得留意Mongo 的「静默失败」字段名错、分页参数错、无索引比报错更危险自检清单里每一项的「怎么验」就是对抗静默失败的手段四个练习任务分别对应索引优化、缓存、存储迁移取舍、配置化降级都是后续章节会真实用到的能力

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑