目录
-
- [一、MongoDB 简介](#一、MongoDB 简介)
-
- [1.1 什么是 MongoDB](#1.1 什么是 MongoDB)
- [1.2 MongoDB vs 关系型数据库对比](#1.2 MongoDB vs 关系型数据库对比)
- [1.3 MongoDB 六大核心优势](#1.3 MongoDB 六大核心优势)
- [1.4 典型应用场景](#1.4 典型应用场景)
- [二、MongoDB 安装与配置](#二、MongoDB 安装与配置)
-
- [2.1 Linux 安装 MongoDB(CentOS 7)](#2.1 Linux 安装 MongoDB(CentOS 7))
- [2.2 Docker 安装 MongoDB](#2.2 Docker 安装 MongoDB)
- [2.3 mongosh 使用](#2.3 mongosh 使用)
- [2.4 安全认证](#2.4 安全认证)
- [2.5 GUI 管理工具推荐](#2.5 GUI 管理工具推荐)
- [三、MongoDB 文档操作(CRUD)](#三、MongoDB 文档操作(CRUD))
-
- [3.1 插入文档](#3.1 插入文档)
- [3.2 查询文档](#3.2 查询文档)
- [3.3 更新文档](#3.3 更新文档)
- [3.4 删除文档](#3.4 删除文档)
- [3.5 批量写操作 bulkWrite](#3.5 批量写操作 bulkWrite)
- [四、MongoDB 高级数据类型](#四、MongoDB 高级数据类型)
-
- [4.1 BSON 协议与数据类型](#4.1 BSON 协议与数据类型)
- [4.2 日期类型](#4.2 日期类型)
- [4.3 ObjectId](#4.3 ObjectId)
- [4.4 内嵌文档与数组](#4.4 内嵌文档与数组)
- [4.5 固定集合(Capped Collection)](#4.5 固定集合(Capped Collection))
- [五、SpringBoot 整合 MongoDB](#五、SpringBoot 整合 MongoDB)
-
- [5.1 准备工作](#5.1 准备工作)
- [5.2 文档操作注解](#5.2 文档操作注解)
- [5.3 CRUD 操作代码](#5.3 CRUD 操作代码)
- [5.4 小技巧:去除 `_class` 字段](#5.4 小技巧:去除
_class字段)
- 六、学习总结
一、MongoDB 简介
1.1 什么是 MongoDB
MongoDB 是一个文档数据库(以 JSON 为数据模型),由 C++ 编写,旨在为 WEB 应用提供可扩展的高性能数据存储解决方案。
文档来自于"JSON Document",数据库中的一条记录就像是一个 PDF、WORD 文档一样。
定位:MongoDB 是一个介于关系型数据库和非关系型数据库之间的产品,是非关系型数据库当中功能最丰富、最像关系型数据库的。
- 存储格式:BSON(Binary JSON,JSON 的二进制扩展格式),支持嵌套文档和数组
- 查询能力:支持的查询语句非常强大,基本可以实现关系型数据库单表查询的绝大部分功能
- 事务支持:支持对文档进行原子性操作(ACID 事务)
MongoDB 在数据库排行榜中位列前5,详情参考:https://db-engines.com/en/ranking
1.2 MongoDB vs 关系型数据库对比
| 关系型数据库 | MongoDB | 说明 |
|---|---|---|
| database | database | 数据库 |
| table | collection | 集合(表),集合中可存放不同结构的文档 |
| row | document | 文档(行),由多个不同字段组成 |
| column | field | 字段(列) |
| index | index | 索引,概念一致 |
| primary key | _id | 每个文档都拥有唯一的 _id 字段 |
| view | view | 视图,MongoDB 3.4 开始提供,通过聚合管道实现 |
| JOIN | $lookup | 聚合操作中实现关联查询(表连接) |
⚠️ 重要区别:MongoDB 没有外键约束,文档内的字段不要求相同------这正是其灵活半结构化的特点。
1.3 MongoDB 六大核心优势
| 优势 | 说明 |
|---|---|
| 🔗 直观 | 从复杂的关系模型到一目了然的对象模型 |
| ⚡ 快速 | 简单可靠的快速开发方式 |
| 🔄 灵活 | 动态模式意味着更容易应对业务变化 |
| 🚀 高可用 | 副本集提供 99.999% 高可用 |
| 📈 可扩展 | 分片架构支持海量数据和无限扩展 |
| 🌐 JSON 友好 | JSON 结构和对象模型接近,开发效率高 |
1.4 典型应用场景
| 场景 | 说明 |
|---|---|
| 🎮 游戏 | 存储游戏用户信息、装备、积分等,直接以内嵌文档形式存储,便于查询 |
| 🛒 物流 | 存储订单信息,订单状态在运送过程中不断更新,MongoDB 以嵌套数组方式存储 |
| 👥 社交 | 存储用户信息及用户发表的朋友圈信息,通过地理位置索引实现附近功能 |
| 📡 物联网 | 存储所有接入的智能设备信息及设备汇报的日志信息 |
| 🎬 视频直播 | 存储用户信息、点赞、弹幕等 |
| 🔬 大数据应用 | 使用云数据库 MongoDB 作为大数据的云存储系统,随时进行数据提取分析 |
二、MongoDB 安装与配置
2.1 Linux 安装 MongoDB(CentOS 7)
前置准备:CentOS 7 系统
下载 MongoDB Community Server:
bash
# 下载 MongoDB
wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel70-6.0.5.tgz
tar -zxvf mongodb-linux-x86_64-rhel70-6.0.5.tgz
启动 MongoDB Server:
bash
# 创建 dbpath 和 logpath
mkdir -p /mongodb/data /mongodb/log
# 进入 mongodb 目录,启动 mongodb 服务
bin/mongod --port=27017 --dbpath=/mongodb/data --logpath=/mongodb/log/mongodb.log \
--bind_ip=0.0.0.0 --fork
常用启动参数说明:
| 参数 | 说明 |
|---|---|
--dbpath |
指定数据文件存放的目录 |
--logpath |
指定日志文件路径(注意:是文件非目录) |
--logappend |
使用追加的方式记录日志 |
--port |
指定端口,默认为 27017 |
--bind_ip |
默认只监听 localhost |
--fork |
后台运行 |
--auth |
开启认证模式 |
配置环境变量:
bash
# 修改 /etc/profile 添加环境变量
export MONGODB_HOME=/usr/local/soft/mongodb
PATH=$PATH:$MONGODB_HOME/bin
# 然后执行
source /etc/profile
配置文件方式启动(推荐):
yaml
# /mongodb/conf/mongo.conf(YAML 格式)
systemLog:
destination: file
path: /mongodb/log/mongod.log
logAppend: true
storage:
dbPath: /mongodb/data
engine: wiredTiger
journal:
enabled: true
net:
bindIp: 0.0.0.0
port: 27017
processManagement:
fork: true
bash
# 使用配置文件启动
mongod -f /mongodb/conf/mongo.conf
# 关闭 MongoDB(方式1)
mongod --port=27017 --dbpath=/mongodb/data --shutdown
# 关闭 MongoDB(方式2:进入 mongosh)
use admin
db.shutdownServer()
2.2 Docker 安装 MongoDB
bash
# 拉取 mongo 镜像
docker pull mongo:6.0.5
# 运行 mongo 容器
docker run --name mongo-server -p 29017:27017 \
-e MONGO_INITDB_ROOT_USERNAME=fox \
-e MONGO_INITDB_ROOT_PASSWORD=fox \
-d mongo:6.0.5 --wiredTigerCacheSizeGB 1
# 远程连接
mongosh ip:29017 -u fox -p fox
💡
--wiredTigerCacheSizeGB 1用于限制内存使用,否则 Mongo 会将 wiredTigerCacheSizeGB 设置为可用内存的较高比例。
2.3 mongosh 使用
mongosh 是 MongoDB 的交互式 JavaScript Shell,MongoDB 6.0 已移除旧版 mongo,改用 mongosh。
bash
# 安装 mongosh(CentOS 7)
wget https://downloads.mongodb.com/compass/mongodb-mongosh-1.8.0.x86_64.rpm
yum install -y mongodb-mongosh-1.8.0.x86_64.rpm
# 连接 MongoDB
mongosh --host=192.168.65.206 --port=27017
mongosh 192.168.65.206:27017
# 指定 URI 格式连接
mongosh mongodb://192.168.65.206:27017/test
常用 mongosh 命令:
| 命令 | 说明 |
|---|---|
show dbs |
显示数据库列表 |
use <数据库名> |
切换/创建数据库 |
db.dropDatabase() |
删除当前数据库 |
show collections |
显示当前数据库的集合列表 |
db.<集合>.stats() |
查看集合信息 |
db.<集合>.drop() |
删除集合 |
show users |
显示当前数据库的用户列表 |
db.version() |
查看数据库版本 |
db.help() |
查询当前数据库支持的方法 |
| `exit | quit` |
javascript
// 数据库操作
show dbs // 查看所有库
use test // 切换到指定数据库,不存在则创建
db.dropDatabase() // 删除当前数据库
// 集合操作
show collections // 查看集合
db.createCollection("emp") // 创建集合
db.emp.drop() // 删除集合
createCollection 选项:
| 字段 | 类型 | 说明 |
|---|---|---|
capped |
Boolean | 是否创建固定集合(超出大小时自动覆盖最早文档) |
size |
数值 | 固定集合时指定最大字节数(capped=true 时必填) |
max |
数值 | 固定集合时指定包含的最大文档数 |
2.4 安全认证
MongoDB 的安全认证通过用户名/密码实现,分两步:先建超级管理员用户,再建普通应用用户。
创建超级管理员:
javascript
use admin
// 创建管理员用户
db.createUser({user: "fox", pwd: "fox", roles: ["root"]})
// 查看用户
show users
// 删除用户
db.dropUser("fox")
内置权限角色:
| 角色 | 说明 |
|---|---|
read |
允许用户读取指定数据库 |
readWrite |
允许用户读写指定数据库 |
dbAdmin |
执行管理函数(索引创建、删除、统计等) |
dbOwner |
数据库最高权限(增删改查+用户管理) |
userAdmin |
在指定数据库中创建、删除和管理用户 |
clusterAdmin |
分片和复制集的管理权限(仅 admin 库可用) |
root |
超级管理员,拥有所有权限 |
readAnyDatabase |
所有数据库的读权限(仅 admin 库) |
readWriteAnyDatabase |
所有数据库的读写权限(仅 admin 库) |
开启认证并连接:
bash
# 启动时开启认证
mongod -f /mongodb/conf/mongo.conf --auth
# 带认证连接
mongosh 192.168.65.206:27017 -u fox -p fox --authenticationDatabase=admin
2.5 GUI 管理工具推荐
| 工具 | 说明 | 地址 |
|---|---|---|
| Compass | 官方 GUI(免费) | https://www.mongodb.com/zh-cn/products/compass |
| Robo 3T | 开源免费 | https://robomongo.org/ |
| Studio 3T | 收费(30天试用) | https://studio3t.com/download/ |
MongoDB Database Tools:
| 工具 | 说明 |
|---|---|
mongostat |
数据库性能监控工具 |
mongotop |
热点集合监控 |
mongodump |
数据库逻辑备份 |
mongorestore |
数据库逻辑恢复 |
mongoexport |
数据导出工具 |
mongoimport |
数据导入工具 |
bsondump |
BSON 格式转换工具 |
mongofiles |
GridFS 文件管理 |
三、MongoDB 文档操作(CRUD)
参考文档:https://www.mongodb.com/docs/manual/reference/sql-comparison/
3.1 插入文档
MongoDB 提供以下方法将文档插入集合:
db.collection.insertOne():将单个文档插入到集合中db.collection.insertMany():将多个文档插入到集合中
插入单个文档 insertOne:
javascript
// 基础语法
db.collection.insertOne(
<document>,
{ writeConcern: <document> }
)
// 示例(包含 writeConcern)
db.emps.insertOne(
{ name: "fox", age: 35 },
{
writeConcern: { w: "majority", j: true, wtimeout: 5000 }
}
)
writeConcern 参数说明:
| 参数 | 说明 |
|---|---|
w |
指定写确认级别:数字=等待完成写操作的节点数;majority=多数节点 |
j |
是否需要日志持久化后才返回(true=日志落盘后返回,false=不等待) |
wtimeout |
等待写操作完成的超时时间(毫秒),默认 0(不超时) |
插入多个文档 insertMany:
javascript
db.collection.insertMany(
[ <document1>, <document2>, ... ],
{
writeConcern: <document>,
ordered: <boolean> // 是否顺序写入,默认 true
}
)
实战:插入 50 本书的数据:
javascript
// 编辑脚本 book.js
var tags = ["nosql", "mongodb", "document", "developer", "popular"];
var types = ["technology", "sociality", "travel", "novel", "literature"];
var books = [];
for (var i = 0; i < 50; i++) {
var typeIdx = Math.floor(Math.random() * types.length);
var tagIdx = Math.floor(Math.random() * tags.length);
var favCount = Math.floor(Math.random() * 100);
var book = {
title: "book-" + i,
type: types[typeIdx],
tag: tags[tagIdx],
favCount: favCount,
author: "xxx" + i
};
books.push(book);
}
db.books.insertMany(books);
// 在 mongosh 中执行脚本
load("books.js")
3.2 查询文档
查询所有文档:
javascript
// 语法
db.collection.find(query, projection)
// query:查询条件(可选)
// projection:返回字段(可选)
// 查询所有
db.books.find()
// 格式化输出
db.books.find().pretty()
// 查询第一条文档
db.collection.findOne(query, projection)
⚠️
projection字段:_id为 1 时总是返回;其他字段要么全为 1(包含),要么全为 0(排除);但_id字段可以单独设 0 排除。
比较查询运算符:
| SQL | MQL |
|---|---|
a = 1 |
{a: 1} |
a <> 1 |
{a: {$ne: 1}} |
a > 1 |
{a: {$gt: 1}} |
a >= 1 |
{a: {$gte: 1}} |
a < 1 |
{a: {$lt: 1}} |
a <= 1 |
{a: {$lte: 1}} |
逻辑查询运算符:
| SQL | MQL |
|---|---|
a = 1 AND b = 1 |
{a: 1, b: 1} 或 {$and: [{a:1},{b:1}]} |
a = 1 OR b = 1 |
{$or: [{a: 1}, {b: 1}]} |
a IS NULL |
{a: {$exists: false}} |
a IN (1, 2, 3) |
{a: {$in: [1, 2, 3]}} |
查询示例:
javascript
// 查询带有 nosql 标签的 book 文档
db.books.find({tag: "nosql"})
// 根据 id 查询单个 book 文档
db.books.find({_id: ObjectId("61caa09ee0782536660494d9")})
// 查询分类为"travel"并且收藏数超过 60 的 book 文档
db.books.find({type: "travel", favCount: {$gt: 60}})
// 正则表达式匹配查询(查询 type 中包含 so 字符的 book)
db.books.find({type: {$regex: "so"}})
// 简写形式
db.books.find({type: /so/})
排序:
javascript
// 指定按收藏数(favCount)降序返回
db.books.find({type: "travel"}).sort({favCount: -1})
// 1 表示升序,-1 表示降序
分页查询:
javascript
// skip 指定跳过的记录数,limit 限制返回结果数
// 查询第 3 页(每页 8 条)
db.books.find().skip(16).limit(8)
🔔 大数据量分页最佳实践 :生产环境中应避免使用 skip/limit 方式,推荐使用游标+唯一索引:
javascript// 第一页 db.books.find({}).sort({_id: 1}).limit(10) // 第二页(基于上页最后一个 _id) db.books.find({_id: {$gt: <上一页最后一个_id>}}).sort({_id: 1}).limit(10)同时,不要在分页中使用 count(),count 需要扫描所有匹配记录,在大数据量下极慢。
3.3 更新文档
MongoDB 提供以下更新方法:
db.collection.updateOne():更新单个文档db.collection.updateMany():更新多个文档
常用更新操作符:
| 操作符 | 语法 | 说明 |
|---|---|---|
$set |
{$set: {field: value}} |
指定字段设值,不存在则创建 |
$unset |
{$unset: {field: 1}} |
删除字段 |
$inc |
{$inc: {field: value}} |
字段值增减(正数增,负数减) |
$rename |
{$rename: {old_name: new_name}} |
修改字段名 |
$push |
{$push: {field: value}} |
向数组末尾追加元素 |
$pushAll |
{$pushAll: {field: value_array}} |
向数组追加多个值 |
$pull |
{$pull: {field: value}} |
从数组删除指定元素 |
$addToSet |
{$addToSet: {field: value}} |
向数组添加元素(去重) |
$pop |
{$pop: {field: 1}} |
删除数组第一个(-1)或最后一个(1)元素 |
更新示例:
javascript
// 将所有 type 为"novel"的文档添加 publishedDate 字段
db.books.updateMany({type: "novel"}, {$set: {publishedDate: new Date()}})
// findAndModify:查询并修改,返回操作前的文档
db.books.findAndModify({
query: {_id: ObjectId("642ec31813bdda928a1ea2a8")},
update: {$inc: {favCount: 1}}
})
// new: true 表示返回修改后的文档
db.books.findAndModify({
query: {_id: ObjectId("642ec31813bdda928a1ea2a8")},
update: {$inc: {favCount: 1}},
new: true
})
updateOne 完整语法:
javascript
db.collection.updateOne(
<filter>, // 筛选条件
<update>, // 更新操作
{
upsert: <boolean>, // true=不存在时插入,默认 false
writeConcern: <document>,
collation: <document>,
arrayFilters: [ <filterdocument1>, ... ],
hint: <document|string> // MongoDB 4.2.1+
}
)
3.4 删除文档
javascript
// deleteOne:删除第一个匹配文档
db.books.deleteOne({type: "novel"})
// deleteMany:删除所有匹配文档
db.books.deleteMany({type: "novel"})
// deleteMany({}):删除集合中全部文档(比 remove 效率高,但不如 drop)
db.books.deleteMany({})
// findOneAndDelete:删除并返回被删除的文档
db.books.findOneAndDelete({type: "novel"})
// 按指定排序删除第一条(实现队列先进先出)
db.books.findOneAndDelete({type: "novel"}, {sort: {favCount: 1}})
⚠️ 如果要删除整个集合,使用
drop()更高效,而不是deleteMany({})。
3.5 批量写操作 bulkWrite
bulkWrite() 提供执行多种插入、更新和删除操作的能力:
javascript
db.pizzas.bulkWrite([
{ insertOne: { document: { _id: 3, type: "beef", size: "medium", price: 6 } } },
{ insertOne: { document: { _id: 4, type: "sausage", size: "large", price: 10 } } },
{ updateOne: {
filter: { type: "cheese" },
update: { $set: { price: 8 } }
} },
{ deleteOne: { filter: { type: "pepperoni" } } },
{ replaceOne: {
filter: { type: "vegan" },
replacement: { type: "tofu", size: "small", price: 4 }
} }
])
bulkWrite() 支持的操作类型:insertOne、updateOne、updateMany、replaceOne、deleteOne、deleteMany
四、MongoDB 高级数据类型
4.1 BSON 协议与数据类型
为什么使用 BSON 而不是 JSON?
JSON 虽通用,但存在以下局限:
- 只支持 6 种基本类型(string、number、object、array、true/false、null)
- 基于文本,解析效率较低
- 不支持日期、二进制等特殊类型
BSON(Binary JSON)是 MongoDB 使用的存储/传输格式,相对 JSON 有以下优势:
| 优势 | 说明 |
|---|---|
| 🔤 类型更丰富 | 支持日期、二进制、ObjectId 等 JSON 不支持的类型 |
| ⚡ 高效编解码 | 二进制格式,记录每个元素长度,可直接 seek 读取 |
| 📦 支持嵌套 | 同样支持嵌套文档和数组 |
MongoDB 中,单个 BSON 文档最大为 16M,文档嵌套层数不超过 100。
常用 BSON 数据类型:
| 类型 | 编号 | 别名 | 说明 |
|---|---|---|---|
| Double | 1 | "double" | 双精度浮点数 |
| String | 2 | "string" | 字符串 |
| Object | 3 | "object" | 对象(内嵌文档) |
| Array | 4 | "array" | 数组 |
| Binary data | 5 | "binData" | 二进制数据 |
| ObjectId | 7 | "objectId" | 对象ID(自动生成) |
| Boolean | 8 | "bool" | 布尔值 |
| Date | 9 | "date" | 日期 |
| Null | 10 | "null" | 空值 |
| Regular Expression | 11 | "regex" | 正则表达式 |
| 32-bit integer | 16 | "int" | 32位整数 |
| Timestamp | 17 | "timestamp" | 时间戳 |
| 64-bit integer | 18 | "long" | 64位整数 |
| Decimal128 | 19 | "decimal" | 高精度浮点(3.4+) |
$type 操作符:
javascript
// 查询 title 字段为 string 类型的文档
db.books.find({"title": {$type: 2}})
// 等价于
db.books.find({"title": {$type: "string"}})
4.2 日期类型
javascript
// 插入三种日期格式
db.dates.insertMany([
{data1: Date()}, // 返回字符串
{data2: new Date()}, // 返回 ISODate 对象(UTC 时间)
{data3: ISODate()} // 同 new Date()
])
db.dates.find().pretty()
⚠️ MongoDB 使用 UTC(+0 时区) 存储日期,查询时注意时区换算(中国时区 +8)。
4.3 ObjectId
每个集合文档都有唯一的 _id 字段,默认使用 ObjectId 类型(12 字节,十六进制表示)。
ObjectId 结构(防重复设计):
[ 4字节 Unix时间戳(秒) ] [ 5字节 随机数(进程+机器唯一标识) ] [ 3字节 递增计数器 ]
| 方法 | 说明 |
|---|---|
ObjectId.str |
返回对象的十六进制字符串表示 |
ObjectId.getTimestamp() |
返回创建时间(日期对象) |
ObjectId.toString() |
以 ObjectId(...) 形式返回字符串 |
ObjectId.valueOf() |
以十六进制字符串 str 形式返回 |
javascript
// 创建一个新的 ObjectId
x = ObjectId()
4.4 内嵌文档与数组
内嵌文档:
javascript
// 插入带作者信息的书籍文档(内嵌)
db.books.insert({
title: "大卫科波菲尔",
author: {
name: "狄更斯",
gender: "男",
hometown: "英国"
}
})
// 查询狄更斯的作品
db.books.find({"author.name": "狄更斯"})
// 修改作者的家乡字段(点表示法访问嵌套字段)
db.books.updateOne(
{"author.name": "狄更斯"},
{$set: {"author.hometown": "英国/台湾"}}
)
数组操作:
javascript
// 设置 tags 数组
db.books.updateOne(
{"author.name": "狄更斯"},
{$set: {tags: ["政治", "历史", "散文", "小说", "文学"]}}
)
// 查询数组中的元素
db.books.find({"author.name": "狄更斯"}, {title: 1, tags: 1})
// $slice 取最后一个 tag
db.books.find({"author.name": "狄更斯"}, {title: 1, tags: {$slice: -1}})
// 追加末尾元素
db.books.updateOne({"author.name": "狄更斯"}, {$push: {tags: "古典"}})
// 同时追加多个元素($each)
db.books.updateOne(
{"author.name": "狄更斯"},
{$push: {tags: {$each: ["人权", "最美文学"]}}}
)
// $slice 保留最后 N 个元素
db.books.updateOne(
{"author.name": "狄更斯"},
{$push: {tags: {$each: ["人权", "最美文学"], $slice: -3}}}
)
// 数组元素查询
db.books.find({tags: "人权"}) // 包含指定元素
db.books.find({tags: {$all: ["人权", "最美文学"]}}) // 同时包含多个元素
嵌套文档型数组(多值属性):
javascript
// 商品多维标签(颜色、尺码、风格)
db.goods.insertMany([{
name: "短袖衫",
tags: [
{tagKey: "size", tagValue: ["M", "L", "XL", "XXL", "XXXL"]},
{tagKey: "color", tagValue: ["红色", "白色"]},
{tagKey: "style", tagValue: "简约"}
]
}])
// 使用 $elemMatch 筛选 color=红色 的商品
db.goods.find({
tags: {
$elemMatch: {tagKey: "color", tagValue: "红色"}
}
})
// 同时匹配 color=红色 AND size=XL
db.goods.find({
tags: {
$all: [
{$elemMatch: {tagKey: "color", tagValue: "红色"}},
{$elemMatch: {tagKey: "size", tagValue: "XL"}}
]
}
})
4.5 固定集合(Capped Collection)
固定集合是一种限制大小的集合,遵循 FIFO 原则,当达到大小上限时自动覆盖最早的文档。
javascript
// 创建固定集合(最多 10 条,4KB)
db.createCollection("logs", {capped: true, size: 4096, max: 10})
// 查看集合状态
db.logs.stats()
// 将普通集合转换为固定集合
db.runCommand({"convertToCapped": "mycoll", size: 100000})
适用场景:
| 场景 | 说明 |
|---|---|
| 📝 系统日志 | 只需固定空间,旧日志自动淘汰 |
| 📊 TopN 数据 | 存储最新的 N 条数据(最新消息、最近浏览等) |
| 📈 实时股票价格 | 存储价格变动信息,配合 tailable cursor 实现实时推送 |
实战:固定集合实现股票价格实时推送:
javascript
// 1. 创建消息队列(10MB)
db.createCollection("stock_queue", {capped: true, size: 10485760})
// 2. 创建时间索引(便于快速检索)
db.stock_queue.createIndex({timestamped: 1})
// 3. 生产者:模拟股票实时变动
function pushEvent() {
while (true) {
db.stock_queue.insert({
timestamped: new Date(),
stock: "MongoDB Inc",
price: 100 * Math.random(1000)
});
print("publish stock changed");
sleep(1000);
}
}
pushEvent()
// 4. 消费者:监听实时股票变动(tailable cursor)
function listen() {
var cursor = db.stock_queue.find({timestamped: {$gte: new Date()}}).tailable();
while (true) {
if (cursor.hasNext()) {
print(JSON.stringify(cursor.next(), null, 2));
}
sleep(1000);
}
}
listen()
💡
.tailable()类似 Linux 的tail -f,游标在获取完所有数据后不会关闭,而是持续等待新数据。
五、SpringBoot 整合 MongoDB
5.1 准备工作
1. 添加依赖:
xml
<!--spring data mongodb-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
2. 配置 yml:
yaml
spring:
data:
mongodb:
uri: mongodb://fox:fox@192.168.65.174:27017/test?authSource=admin
# 也可以分开配置:
# database: test
# host: 192.168.65.174
# port: 27017
# username: fox
# password: fox
# authentication-database: admin
3. 注入 MongoTemplate:
java
@Autowired
MongoTemplate mongoTemplate;
4. 集合操作示例:
java
@Test
public void testCollection() {
boolean exists = mongoTemplate.collectionExists("emp");
if (exists) {
mongoTemplate.dropCollection("emp"); // 删除集合
}
mongoTemplate.createCollection("emp"); // 创建集合
}
5.2 文档操作注解
| 注解 | 作用域 | 说明 |
|---|---|---|
@Document |
类 | 标注类为 MongoDB 文档,collection 属性指定集合名 |
@Id |
成员变量 | 将字段值映射为文档的 _id |
@Field |
成员变量 | 指定字段映射的 key 名(默认为成员变量名) |
@Transient |
成员变量 | 标注字段不参与文档的序列化/反序列化 |
实体类示例:
java
@Document("emp") // 对应 emp 集合中的一个文档
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Employee {
@Id // 映射文档中的 _id
private Integer id;
@Field("username") // 映射为 username 字段
private String name;
@Field
private int age;
@Field
private Double salary;
@Field
private Date entryDay;
}
5.3 CRUD 操作代码
插入文档:
java
@Test
public void testInsert() {
Employee employee = new Employee(1, "小明", 30, 10000.00, new Date());
// save: _id 存在时更新,不存在时插入
// mongoTemplate.save(employee);
// insert: _id 存在时抛异常,支持批量插入
mongoTemplate.insert(employee);
List<Employee> list = Arrays.asList(
new Employee(2, "张三", 21, 5000.00, new Date()),
new Employee(3, "李四", 26, 8000.00, new Date()),
new Employee(4, "王五", 22, 8000.00, new Date())
);
mongoTemplate.insert(list, Employee.class);
}
📌 insert vs save 区别:
insert:重复 _id 时抛DuplicateKeyException;支持一次批量插入(效率高)save:已存在的文档会更新;多条数据需逐条调用(效率低)
查询文档:
java
@Test
public void testFind() {
// 查询全部
List<Employee> list = mongoTemplate.findAll(Employee.class);
// 根据 _id 查询
Employee e = mongoTemplate.findById(1, Employee.class);
// 条件查询(薪资 > 8000)
Query query = new Query(Criteria.where("salary").gte(8000));
// 范围查询(薪资 4000 ~ 10000)
// Query query = new Query(Criteria.where("salary").gt(4000).lt(10000));
// 模糊查询
// Query query = new Query(Criteria.where("name").regex("三"));
// AND/OR 组合查询
Criteria criteria = new Criteria();
// AND:年龄>25 且 薪资>8000
// criteria.andOperator(Criteria.where("age").gt(25), Criteria.where("salary").gt(8000));
// OR:名字叫张三 或 薪资>5000
criteria.orOperator(Criteria.where("name").is("张三"), Criteria.where("salary").gt(5000));
Query query = new Query(criteria);
// 排序 + 分页
query.with(Sort.by(Sort.Order.desc("salary")))
.skip(0) // 跳过的记录数
.limit(4); // 每页显示条数
List<Employee> employees = mongoTemplate.find(query, Employee.class);
employees.forEach(System.out::println);
}
// 使用 JSON 字符串查询
@Test
public void testFindByJson() {
// 条件查询:年龄>25 或 薪资>=8000
String json = "{$or:[{age:{$gt:25}},{salary:{$gte:8000}}]}";
Query query = new BasicQuery(json);
List<Employee> employees = mongoTemplate.find(query, Employee.class);
}
更新文档:
java
@Test
public void testUpdate() {
Query query = new Query(Criteria.where("salary").gte(15000));
Update update = new Update();
update.set("salary", 13000);
// updateFirst:只更新第一条匹配记录
// mongoTemplate.updateFirst(query, update, Employee.class);
// updateMulti:更新所有匹配记录
// mongoTemplate.updateMulti(query, update, Employee.class);
// upsert:没有匹配记录时新建
// update.setOnInsert("id", 11); // 指定 _id
UpdateResult updateResult = mongoTemplate.upsert(query, update, Employee.class);
System.out.println(updateResult.getModifiedCount()); // 修改的记录数
}
删除文档:
java
@Test
public void testDelete() {
// 删除所有文档
// mongoTemplate.remove(new Query(), Employee.class);
// 条件删除(薪资 >= 10000)
Query query = new Query(Criteria.where("salary").gte(10000));
mongoTemplate.remove(query, Employee.class);
}
5.4 小技巧:去除 _class 字段
Spring Data MongoDB 默认会在文档中存储 _class 字段(Java 类全限定名)用于反序列化,如需去除:
java
@Configuration
public class MongoConfig {
/**
* 配置 TypeMapper 去除 _class 字段
*/
@Bean
MappingMongoConverter mappingMongoConverter(
MongoDatabaseFactory mongoDatabaseFactory,
MongoMappingContext context,
MongoCustomConversions conversions) {
DbRefResolver dbRefResolver = new DefaultDbRefResolver(mongoDatabaseFactory);
MappingMongoConverter mappingMongoConverter = new MappingMongoConverter(dbRefResolver, context);
mappingMongoConverter.setCustomConversions(conversions);
// 设置 DefaultMongoTypeMapper,将 typeKey 设置为 null(即不存储 _class)
mappingMongoConverter.setTypeMapper(new DefaultMongoTypeMapper(null));
return mappingMongoConverter;
}
}
六、学习总结
核心知识点回顾
MongoDB
├── 基础概念
│ ├── 文档数据库(BSON/JSON 模型)
│ ├── Database → Collection → Document → Field
│ └── 灵活半结构化,无强制 Schema
│
├── 安装部署
│ ├── Linux(CentOS 7)直接安装
│ ├── Docker 快速部署(推荐)
│ └── 安全认证(--auth + 用户权限体系)
│
├── CRUD 操作
│ ├── 插入:insertOne / insertMany
│ ├── 查询:find / findOne + 丰富操作符
│ ├── 更新:updateOne / updateMany / findAndModify
│ └── 删除:deleteOne / deleteMany / findOneAndDelete
│
├── 高级数据类型
│ ├── BSON(Binary JSON,比 JSON 类型更丰富)
│ ├── ObjectId(自带时间戳的唯一 ID)
│ ├── Date(UTC 存储)
│ ├── 内嵌文档 & 数组
│ └── 固定集合(capped collection)
│
└── SpringBoot 整合
├── spring-boot-starter-data-mongodb
├── @Document / @Id / @Field 注解
├── MongoTemplate API
└── Criteria 条件构造
重要注意事项
- 🔑 安全 :生产环境务必开启
--auth,并为不同应用创建最小权限用户- 📏 文档大小:单文档最大 16MB,嵌套层数不超过 100 层
- ⏰ 时区:MongoDB 存储 UTC 时间,应用层需做 +8 处理
- 📄 分页:大数据量使用游标分页而非 skip/limit,避免性能问题
- 🗑️ 删除 :删除整个集合用
drop(),比deleteMany({})更高效- 🏷️ _class :SpringBoot 默认存储
_class字段,可通过配置去除
📖 参考资料
- MongoDB 官方文档:https://www.mongodb.com/docs/manual/
- MongoDB 6.0 Release Notes:https://www.mongodb.com/docs/v6.0/release-notes/6.0/
- Spring Data MongoDB:https://docs.spring.io/spring-data/mongodb/docs/current/reference/html
- SQL vs MQL 对照:https://www.mongodb.com/docs/manual/reference/sql-comparison/
学习目标:掌握 MongoDB 的核心概念、安装配置、CRUD 操作、数据类型及 SpringBoot 整合开发
参考资料:MongoDB 官方文档 https://www.mongodb.com/docs/manual/