1. 装MongoDB之前的判断:选MSI安装包还是ZIP免安装包
先说结论:如果你在Windows上装MongoDB总是失败,八成不是操作不对,而是安装包选错了。很多Java开发者第一次接触MongoDB,习惯性地去官网下载最新版MSI安装包,一路Next,结果要么服务起不来,要么连不上客户端,要么重启机器后MongoDB服务神秘消失。我前前后后在Windows环境里搭过不下十次MongoDB,踩了一轮坑之后,才摸索出一套适合自己的方式。
MongoDB官方对Windows提供了两种主要分发形式:一个是传统的MSI交互式安装包,另一个是ZIP免安装压缩包。这两种方式的底层逻辑不同,适合的人群也不同。MSI安装包会把MongoDB注册成Windows服务,自动处理数据目录、日志目录、配置文件的默认位置,还会往系统Path里写入环境变量,看起来最省事。但它有一些隐藏问题:MSI安装包默认绑定的服务账户是Network Service,这个账户对某些自定义目录的权限处理得很别扭,加上新版MongoDB默认开启了绑定localhost的配置,如果你在安装时没注意配置项,装完以后连接字符串里带了主机名或IP,就会直接报错。
而ZIP免安装包则把一切主动权交给你:下载解压、手动创建数据目录、手动启动mongod进程、手动注册服务。听起来麻烦,但胜在可控。尤其是对于一个要长期在Windows上做开发的Java工程师来说,ZIP包配合配置文件,可以让你在同一台机器上跑多个版本的MongoDB,或者随意指定数据目录和端口,这在做项目联调时特别有用。
我当时选择ZIP包还有一个很现实的原因:公司Windows开发机上没有管理员权限,MSI安装到Program Files需要提权,而ZIP包解压到用户目录完全不需要管理员权限。后来我在给团队成员搭环境时也发现,通过ZIP包安装能少很多“安装完服务没有注册成功”之类的幺蛾子。所以我个人强烈建议Java开发者选择ZIP免安装方式,除非你只是临时用一下、完全不在意细节。
当然,如果你已经下载了MSI安装包,也不是不能用。但你要记住几个关键点:安装时不要勾选“Install MongoD as a Service”之外的其他额外组件(比如Compass),安装完成后检查数据目录的权限,以及在服务属性里把登录身份从“网络服务”改成“本地系统”,否则后续写入数据时会经常碰壁。
1.1 下载前先确认版本选择逻辑
MongoDB的版本号演进很快,从4.x到5.x再到6.x、7.x,每个大版本在行为上都有细微变化。作为Java开发者,你不需要追求最新版,而应该关注SpringBoot Data MongoDB的兼容性。SpringBoot 2.7系列官方支持的是MongoDB 4.x/5.x,SpringBoot 3.x系列则可以兼容5.x/6.x甚至7.x。如果你用的是SpringBoot 2.5.4这种老版本,硬要跑去连MongoDB 7.0,虽然大部分CRUD能跑通,但认证机制和聚合管道的一些细节可能会出问题。
稳妥的做法是:先看SpringBoot版本的BOM里面对应的MongoDB Driver版本,再决定MongoDB Server版本。具体怎么看?打开项目的pom.xml,找到spring-boot-starter-data-mongodb依赖,点进去看mongodb-driver-sync的版本。比如spring-boot 2.7.18里内置的是mongodb-driver-sync 4.6.1,这个驱动对接MongoDB 5.0完全没问题,对接6.0也可以。如果是spring-boot 3.2.x,内置的驱动是4.11.x,对接MongoDB 7.0毫无压力。
官网下载页面把版本分成三类:Current(当前推荐)、Reliability(可靠性,通常指上一个稳定版)、Development(开发版)。Java开发环境别选Development,老老实实选Reliability,图个稳定。下载时注意两个维度:Platform选Windows,Package选zip。下载文件名通常是mongodb-windows-x86_64-7.0.14.zip这种格式,几秒钟就能下载下来。另外提醒一下,不要下载那个带“msi”后缀的,除非你真的想用安装向导。
1.2 数据目录与日志目录的规划
MongoDB的数据目录规划,在Windows上比在Linux上更容易被忽视。Linux下大家习惯用/data/db,而Windows下很多人直接把数据目录放在安装包解压目录下。这本身没错,但有两个隐患:第一,解压目录可能在C盘,C盘空间不足时MongoDB会直接挂掉;第二,如果解压目录被安全软件清理或者被你随手移动,数据就找不回来了。
我习惯的做法是:在某个固定的磁盘根目录下创建一个专门的MongoDB目录,比如D:\MongoDB,下面再分成server、data、logs三个子目录。server目录放解压后的二进制文件,data目录放数据库文件,logs目录放日志。这样做的好处是:升级MongoDB版本时只需要替换server目录里的文件,数据目录完全不受影响;如果以后要切换不同版本,也可以把server目录改名成server_7014,再放下一个版本,互不干扰。
规划数据目录时还要考虑磁盘性能。MongoDB的写入频次很高,如果你的代码里大量使用upsert或批量写入,数据目录所在的磁盘转速和IO延迟直接影响写入性能。建议把数据目录放到固态硬盘上,至少不要放在移动硬盘或网络驱动器中。另外注意路径中不要有中文和空格,Clone路径和MongoDB进程对中文路径的支持偶尔会抽风,别给自己埋雷。
2. 一步步把MongoDB跑起来:安装、服务与连接验证
这个章节咱们直接上实操。我用ZIP包示例,但如果你已经下载了MSI包,其实后面的配置思路也是相通的,核心就是把mongod进程正常拉起来。
2.1 解压与目录配置实操
假设你已经把mongodb-windows-x86_64-7.0.14.zip下载到了D盘。右键解压到D:\MongoDB\server,解压完成后你会看到bin目录,里面包含mongod.exe、mongos.exe、mongo.exe或mongosh.exe等文件。这里注意:MongoDB 6.0之后,自带的客户端交互工具不再叫mongo.exe,而是换成了mongosh.exe(MongoDB Shell)。如果你下载的版本里面没有mongo.exe,不要慌,要么你下载的是6.0以上版本,要么说明你还需要单独安装MongoDB Shell。为了省事,我会直接推荐你下载版本时确认包内是否包含mongosh.exe。
接下来创建数据目录和日志目录。我们在D盘创建D:\MongoDB\data和D:\MongoDB\logs。随后,在D:\MongoDB目录下新建一个配置文件mongod.cfg。我习惯把配置都写到文件里,而不是启动时堆一长串命令行参数,这样以后查看起来特别清晰。配置文件内容如下:
systemLog: destination: file path: D:\MongoDB\logs\mongod.log logAppend: true storage: dbPath: D:\MongoDB\data net: bindIp: 127.0.0.1 port: 27017简单解释一下这段配置:systemLog指定了日志文件的输出路径,logAppend表示日志追加写入而不是覆盖;storage.dbPath就是数据库文件存放路径;net.bindIp这里绑定127.0.0.1,只允许本机访问。如果你需要局域网内其他机器连你的MongoDB,可以把bindIp改成0.0.0.0,但这会暴露安全风险,建议配合后面讲的用户认证一起使用。
写完配置后,先不要急着注册Windows服务。用命令行进入bin目录,执行一条前台启动命令验证配置是否正确:
mongod --config D:\MongoDB\mongod.cfg会看到一堆启动日志刷出来,最后出现类似“Waiting for connections”之类的字样,说明配置没问题。如果启动失败,去看D:\MongoDB\logs\mongod.log,里面会写明失败原因。最常见的失败原因是dbPath目录不存在,MongoDB不会自动创建多级目录,只创建最后的dbPath本身,所以你要确保D:\MongoDB\data目录已经存在。
2.2 注册为Windows服务并设置开机自启
验证前台启动没问题后,就可以把mongod注册成Windows服务了。注册服务的方式有几种,传统的做法是用sc命令,但更推荐用mongod自带的--install参数,它内部会调用服务管理接口,还会自动帮你生成服务描述信息。
打开管理员命令行(注意一定要管理员权限),切到bin目录,执行:
mongod --config "D:\MongoDB\mongod.cfg" --serviceName "MongoDB" --serviceDisplayName "MongoDB Server" --install这里指定了服务名和服务显示名。--install参数会根据配置文件生成一个Windows服务,服务启动时会自动加载我们写的mongod.cfg。如果执行成功,命令行会提示“Service successfully installed”。然后打开服务管理器,找到MongoDB服务,右键启动,或者直接用命令:
net start MongoDB启动后再次查看日志文件,确认没有报错。如果不放心,可以用客户端工具连一下。前面说过新版本客户端命令是mongosh,在bin目录下执行:
mongosh --host 127.0.0.1 --port 27017如果能够进入mongosh交互界面,说明服务已经正常运行。界面里会显示当前MongoDB的版本号,并提示“MongoDB server listening”之类的信息。此时你已经完成了最核心的安装步骤。
这里要提醒一件事:注册服务时,如果之前用前台方式启动过mongod,请先把那个前台进程关掉,否则服务启动时可能因为端口被占用而失败。还有一种常见问题是服务启动后又自动停止,查看系统事件日志和mongod.log,往往是因为dbPath权限不足。如果你确认目录确实存在但依然报错,那就把dbPath目录的Full Control权限授予当前登录用户或Everyone,基本上能解决。
2.3 客户端连接验证与版本确认
连接验证不只是看一眼能进去就完事,建议执行几个基础命令确认服务状态。进入mongosh后,执行:
db.runCommand({ ping: 1 })如果返回{ ok: 1 },说明服务进程和数据读写正常。再执行:
db.version()返回当前版本号字符串,比如“7.0.14”。接着我们来验证数据库文件的真实落盘情况。创建一个测试库,插入一条测试数据,然后退出再重进,确认数据不丢。操作如下:
use testdb db.user.insertOne({ name: "java-demo", age: 1 }) show dbs执行完后,打开D:\MongoDB\data目录,你会看到WiredTiger相关文件和collection-numeric开头的一些文件,这表示数据已经写到了磁盘上。再退出mongosh,重新执行mongosh连接,查一下刚才的数据:
use testdb db.user.find()能看到插入的记录,说明整个链路是通的。有些朋友在这里会遇到一种情况:数据插入成功,但重启MongoDB服务后数据消失了。这通常是因为在Windows服务属性里没有正确指定数据目录,或者你启动mongod时使用了命令行参数而忽略了配置文件里的dbPath。检查一下服务启动的命令行里是否带了--config,不要用其它命令手动启动服务。
3. 数据操作入门:CLI常用命令与用户权限配置
服务跑起来之后,往下的内容就开始好玩了。这一节我们聊聊开发阶段最常用的命令,以及提前把权限体系搭起来的必要性。
3.1 数据库、集合与基础CRUD命令
MongoDB里没有传统意义上的“表”,取而代之的是集合(Collection),文档(Document)是JSON格式的。第一次接触的Java开发者,往往会把集合类比成MySQL的表,把文档类比成行,这个类比在90%的场景下都是对的,但有一个重要区别:MongoDB的集合不需要预先定义结构,你可以随时插入形状不同的文档。
进入mongosh后,先看看有哪些数据库:
show dbs新建一个数据库不需要create语句,直接use就行:
use javashop此时创建数据库并不会真的落盘,只有往里面插入第一条数据时数据库才真正创建。接着在数据库里创建一个集合并插入数据:
db.users.insertOne({ name: "张三", age: 28, skills: ["Java", "SpringBoot"] }) db.users.insertMany([ { name: "李四", age: 25, skills: ["MongoDB"] }, { name: "王五", age: 32, skills: ["Java", "Docker"] } ])查询时注意find的用法:
db.users.find() db.users.find({ age: { $gt: 26 } }) db.users.find({ skills: "Java" })更新文档要区分updateOne和updateMany,避免误更新全部记录:
db.users.updateOne({ name: "张三" }, { $set: { age: 29 } })删除时直接传条件即可:
db.users.deleteOne({ name: "王五" })这些命令都是最基础的,但也是后面使用SpringBoot Data MongoDB时各种Query方法的底层语义。建议熟练到不用想就能写出来,因为排查问题时,我经常直接通过mongosh手工构造数据来复现Bug。
3.2 为什么开发环境也要启用用户认证
很多人在本地开发时关掉认证,图省事,觉得MongoDB只绑定127.0.0.1就安全了。这个想法在开发机上问题不大,但有一个隐患:你如果装了Compass之类的可视化工具,或者是SpringBoot应用里用的连接串包含了账户密码,一旦别人拿到你机器上的配置文件,就有可能连上来进行破坏。更重要的是,如果你后续需要把数据导出给前端同事或者部署到测试环境,没有认证的MongoDB暴露在局域网里几乎等于裸奔。
我推荐的开发实践是:从一开始就创建一个管理员用户,然后开启认证。这样你的开发环境行为和生产环境高度一致,等到上线时不会出现“本地连不上,生产也连不上”的手忙脚乱。
创建管理员的流程分为两步。第一步,在未开启认证的模式下连接到MongoDB,切换到admin库,创建用户:
use admin db.createUser({ user: "admin", pwd: "Admin@123456", roles: [ { role: "root", db: "admin" } ] })第二步,修改mongod.cfg,在net下面加上:
security: authorization: enabled然后重启MongoDB服务:
net stop MongoDB net start MongoDB重启之后,再直接用mongosh连接就会报错或提示需要认证。此时连接命令变成:
mongosh --host 127.0.0.1 --port 27017 -u admin -p Admin@123456 --authenticationDatabase admin进入后执行db.runCommand({ ping: 1 })验证权限。如果你在SpringBoot配置里面连接MongoDB,连接串写法也要带上认证参数,比如:
spring.data.mongodb.uri=mongodb://admin:Admin@123456@127.0.0.1:27017/javashop?authSource=admin注意这里面的特殊字符。如果密码里有@、:、/这些符号,需要做URL编码,否则连接串解析会出错。这也是Java开发者经常踩的坑。
3.3 开发库与测试库的权限隔离
创建完root管理员后,不建议直接在SpringBoot里用root账户连业务数据库。更好的做法是单独创建业务用户,并只授予目标数据库的读写权限。比如项目里的数据库叫javashop,可以这样创建:
use javashop db.createUser({ user: "javashop_user", pwd: "Javashop@123", roles: [ { role: "readWrite", db: "javashop" } ] })这样做的好处是:应用账户只对javashop库有读写权限,就算连接串泄露,攻击者也无法通过这个账户去删除其他数据库。同时,在排查慢查询或异常写入时,你能通过日志里的用户名快速定位到是哪个应用在操作。
另外,MongoDB 4.0之后的版本还支持事务。如果你在SpringBoot中使用了@Transactional注解操作MongoDB,要注意MongoDB的事务只支持副本集模式,单机Standalone模式不支持事务。如果你想在Windows开发机上体验事务,需要将MongoDB配置成单节点副本集,方法是在mongod.cfg里加上:
replication: replSetName: rs0重启服务后,用mongosh执行rs.initiate(),之后就变成副本集模式了。这一步在后续实现多文档事务时会非常关键。
4. SpringBoot集成MongoDB:从pom到第一个接口
到这里,安装和基础命令都通了,接下来进入Java开发者最关心的环节:SpringBoot怎么把数据写到MongoDB里?这一节我按照项目实际推进的顺序来写,不会只贴一段JPA代码就结束。
4.1 Maven依赖的版本取舍
SpringBoot整合MongoDB不需要额外引入第三方ORM,官方提供的spring-boot-starter-data-mongodb已经足够。在你项目的pom.xml里添加:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-mongodb</artifactId> </dependency>如果你用的是SpringBoot 2.x,驱动版本通常由Spring Boot BOM统一管理,不用在pom里指定。但如果你希望用更新的MongoDB驱动特性,可以单独加依赖覆盖版本,不过我不建议在生产中这么干。还有一种情况是你想要用到MongoDB特有的聚合管道类型安全API,那需要引入:
<dependency> <groupId>org.mongodb</groupId> <artifactId>mongodb-driver-sync</artifactId> </dependency>这一依赖在starter里已经传递引入了,不需要显式声明。知识要讲清楚:starter里已经有driver-core和bson等库,不需要重复添加。新手容易犯的错误是,在pom里额外添加一堆mongodb驱动依赖,结果版本冲突,启动时NoClassDefFoundError。记住,只需要一个starter即可。
4.2 在application.yml里配置连接信息
SpringBoot 2.x之后,配置项主要用spring.data.mongodb.uri统一配置,老式的host/database/port写法虽然还能用,但不太推荐了。我的配置示例:
spring: data: mongodb: uri: mongodb://javashop_user:Javashop@123@127.0.0.1:27017/javashop?authSource=javashop如果开启了认证就写好用户名密码和authSource,如果没开启认证就直接写:
spring: data: mongodb: uri: mongodb://127.0.0.1:27017/javashop配置好之后,在SpringBoot启动类上加不加@EnableMongoRepositories都不影响使用,因为Spring Boot会自动配置。但如果你自定义了MongoTemplate或MongoClient,就必须自己包一层配置类。对于大多数项目,我建议直接使用由Spring Boot自动装配的MongoTemplate,它已经足够强大,而且不会有人为bug。
4.3 实体类与Repository的编码范例
MongoDB的实体类不要求继承任何父类,就是一个简单的POJO。比如用户文档,可以写成:
@Data @Document(collection = "users") public class User { @Id private String id; private String name; private Integer age; private List<String> skills; }@Document注解指明了集合名。@Id对应的字段映射到MongoDB的_id字段,默认情况下Spring Data会为我们生成ObjectId字符串,你也可以在插入时手动指定。
然后创建一个Repository接口,继承MongoRepository:
public interface UserRepository extends MongoRepository<User, String> { List<User> findByName(String name); List<User> findByAgeGreaterThan(Integer age); }在Service里注入该Repository即可使用。注意,findByName这种命名式查询在底层会解析成对应的Query,如果字段名复杂,不如直接在方法上写@Query注解来得直白。
如果你需要更复杂的操作,比如聚合、批量更新,建议直接注入MongoTemplate:
@Service public class UserService { @Autowired private MongoTemplate mongoTemplate; public List<User> findBySkill(String skill) { Query query = new Query(); query.addCriteria(Criteria.where("skills").is(skill)); return mongoTemplate.find(query, User.class); } }MongoTemplate的使用灵活度更高,也更容易理解底层发生了什么。我的经验是,简单的CRUD用Repository,涉及多表关联查询聚合的,一律MongoTemplate。
4.4 写一个接口并验证数据落库
接着我们写一个最简单的RestController来落库和查询:
@RestController @RequestMapping("/users") public class UserController { @Autowired private UserRepository userRepository; @PostMapping public User create(@RequestBody User user) { return userRepository.insert(user); } @GetMapping("/{name}") public List<User> getByName(@PathVariable String name) { return userRepository.findByName(name); } }启动SpringBoot应用,用Postman或curl调用:
curl -X POST http://localhost:8080/users -H "Content-Type: application/json" -d "{\"name\":\"JavaDeveloper\",\"age\":30,\"skills\":[\"Java\",\"MongoDB\"]}"返回JSON包含id。然后调GET接口:
curl http://localhost:8080/users/JavaDeveloper能查到刚才的数据,说明SpringBoot应用已经通过MongoDB Driver连上了Windows上运行的MongoDB服务。这时候你可以再打开mongosh,用命令查看数据:
use javashop db.users.find()两边看到的应该一致。到这里,从Windows安装MongoDB到SpringBoot集成整体链路已经完全打通。
5. Windows环境下最容易踩的坑与排查链路
这一章可以说是全文的精华。下面这些坑都是我实际踩过或者在帮别人排查时遇到的,把它写成一个一个独立的排查链路,遇到问题时可以照着走一遍。
5.1 端口占用:27017被其他程序抢先绑定
MongoDB启动报错里有一条很典型,日志会写“address already in use”或“Failed to bind 127.0.0.1:27017”。Windows环境下,27017端口被别的东西占用的概率不低,尤其是一些开发工具内置了MongoDB服务或者某些破解软件会偷偷跑一个MongoDB实例。
排查链路:先确认谁占了27017端口。管理员命令行下执行:
netstat -ano | findstr 27017会输出一行记录,最右边是PID。然后查这个PID对应的进程:
tasklist | findstr 12345如果这个进程不是你的MongoDB服务,确认无害后可以直接:
taskkill /PID 12345 /F然后重启MongoDB服务。如果占用的进程就是MongoDB自己,说明你机器上可能已经通过MSI或其他方式装过另一个MongoDB。这时候不要盲目杀掉,先检查是不是当前正在用的服务在跑。如果是老版本的无用服务,建议去服务管理器里停掉并禁用,或者修改当前服务的端口到27018避免冲突。
另外一个容易忽略的点是,Windows下安装过Docker Desktop并且开了容器,而容器内部映射了宿主机27017端口,也会导致占用。这种情况下你apt一下Docker的端口映射配置或者关掉对应容器即可。
5.2 MongoDB服务启动后秒退的根因分析
服务安装成功,手动启动却弹出“服务启动后停止”的提示,去事件查看器里可能什么明确日志都没有,只有一条“MongoDB服务启动失败”。这种问题尤其在裸装ZIP包后立刻注册服务的情况下出现。
核心原因一般有三个:dbPath目录不存在、配置文件格式错误、日志路径不可写。排查链路很简单:因为服务是由配置文件驱动的,所以你要先在命令行下用同样的配置前台启动试试:
mongod --config D:\MongoDB\mongod.cfg前台启动会把错误直接打到终端里。如果你看到的错误是“dbPath must be an existing directory”,那就先把目录创建好,确保路径不含引号。如果是“unrecognized option: security”,说明你配置文件里的缩进或字段名写错了,YAML格式对空格敏感,这里的“security”下面要跟两个空格,不能是Tab。如果日志写入路径D:\MongoDB\logs不存在,也会报错。说到底,配置文件里的路径必须是实际存在的目录,MongoDB没有自动mkdir的仁慈。
如果命令行下用配置文件能启动,但注册成Windows服务就是起不来,大概率是服务账户权限问题。打开服务管理器,找到MongoDB服务,右键属性,在“登录”选项卡里改为“本地系统账户”。改完后再启动服务,基本就能解决权限类的怪问题。
5.3 身份验证失败:一个字符引发的血案
SpringBoot应用连接MongoDB,报错信息通常是“Authentication failed”或者“Command failed with error 13 (Unauthorized)”。很多人第一反应是用户名或密码错了,但往往真正的问题是连接串里的authSource指定不对。
解释一下authSource的含义:它表示验证用户的数据库。比如你是在admin库下创建的root账户,那么连接字符串里必须带?authSource=admin,否则驱动默认会去连接目标库(javashop)去验证名称为admin的用户,结果自然验证失败。所以排查思路是:先在mongosh里手动用用户名密码连一下,确认凭据本身没问题。命令:
mongosh mongodb://javashop_user:Javashop@123@127.0.0.1:27017/javashop?authSource=javashop如果这条命令能成功,那问题就在SpringBoot的配置里。此时检查application.yml里的uri是否被YAML转义破坏。比如密码中包含&符号,YAML中会被当作特殊字符,需要用单引号包裹整个URI:
spring: data: mongodb: uri: 'mongodb://javashop_user:Javashop&123@127.0.0.1:27017/javashop?authSource=javashop'还有一种情况是,你在mongosh中创建用户时误把密码敲成了中文输入法状态下的字符,肉眼看着一样,实际编码不同。这种情况最常见的战场就是密码里的“@”符号,看起来都是半角,但还是建议用一些可视化工具确认一下。
5.4 Windows下文件路径反斜杠与配置文件解析
MongoDB配置文件mongod.cfg使用YAML格式,YAML里处理Windows路径时,如果直接写D:\MongoDB\data,反斜杠会被当作转义字符处理,导致路径解析失败。这也是新手特别喜欢踩的坑,报错信息五花八门,有时候是“Bad escape”,有时候是“unknown option: storage.dbPath”。
正确写法是使用正斜杠:D:/MongoDB/data,或者把整个路径放在引号里:"D:\\MongoDB\\data"。我推荐都用正斜杠,简单不易错。例如:
storage: dbPath: D:/MongoDB/data systemLog: path: D:/MongoDB/logs/mongod.log这个坑在我第一次把Linux上的配置文件搬到Windows时踩过,后来凡是Windows配置,我统一先写正斜杠。另外,配置文件里也不要出现中文注释,Windows下如果文件编码不是UTF-8,中文字符可能会让解析报错。如果你非要写注释,请用英文。
5.5 SpringBoot时区与ObjectId序列化问题
集成MongoDB之后,Java后端最大的困扰之一是ObjectId在JSON序列化时输出的格式不是想要的字符串,而是{"timestamp": ..., "date": ...}这样的嵌套结构,前端解析起来很头疼。这个问题在SpringBoot中使用Jackson时尤其明显。
解决方法很简单,在配置类里注册一个自定义的Serializer,或者更简洁的做法是在实体类的@Id字段上使用JsonSerialize注解特殊处理。我在项目里习惯引入一个全局的MongoDB配置类:
@Configuration public class MongoConfig { @Bean public Jackson2ObjectMapperBuilderCustomizer mongoJacksonCustomizer() { return builder -> builder.serializerByType(ObjectId.class, new JsonSerializer<ObjectId>() { @Override public void serialize(ObjectId value, JsonGenerator gen, SerializerProvider serializers) throws IOException { gen.writeString(value.toHexString()); } }); } }这样所有ObjectId字段序列化时都会输出字符串。时区问题则体现在Date类型上,Java的Date映射到MongoDB是BSON Date,存储的是UTC毫秒值。如果前后端传输出现8小时偏差,一般不是MongoDB的问题,而是Jackson配置的时区问题。在application.yml里设置:
spring: jackson: time-zone: GMT+8基本就能消除。
5.6 大文档与批量写入的注意点
最后一个坑,也是我最近在项目里遇到的:往MongoDB插入一个很大的文档(比如包含Base64图片字符串),SpringBoot直接报“document too large”。这是MongoDB的硬限制,单文档最大16MB。对于文件、图片这类数据,合理的方式是存储在GridFS里,或者干脆只存元数据,文件本体丢到对象存储。
批量写入时,MongoTemplate的insertAll方法如果中间有一条数据出现错误,默认会中断后续写入。Spring Data提供了相关选项,可以在InsertManyOptions里设置ordered(false)让它继续写入,但要注意部分成功的处理逻辑。
如果写入量很大,比如每秒几千条记录,建议不要一条条insertOne,而是用bulkWrite一次性批量提交,能显著提升吞吐量。这一点在Windows开发机上虽然不明显,但到了Linux服务器上的生产环境,差距会非常明显。
我真的建议所有Java开发者在Windows下好好折腾一次MongoDB。把上面这些坑走一遍,你理解的不仅是安装步骤,更是MongoDB的配置体系、权限认证机制和SpringData的底层逻辑。以后再换到Linux服务器上部署,你会发现几乎所有Windows下的概念都能平移到Linux,唯一的区别就是systemd配置和Windows服务配置的形式不同。我当初在Windows上把MongoDB玩明白之后,再去看生产环境里的Linux Mongo部署,基本一路绿灯。如果你也想少走弯路,就从今天的ZIP包安装开始吧。