MongoDB初学者教程:从零开始掌握MongoDB(第五阶段:Java集成实践)

17 阅读9分钟

MongoDB初学者教程:从零开始掌握MongoDB(第五阶段:Java集成实践)

欢迎进入MongoDB学习的第五阶段:Java集成实践现在,我们要将这些知识整合到一个真实的Java项目中,使用Spring BootSpring Data MongoDB构建一个用户管理系统,展示如何在现代Web开发中高效使用MongoDB。

本阶段将带你:

  • 使用Spring Data MongoDB实现Repository模式和自定义查询。
  • 启用审计功能(自动记录创建时间等)。
  • 实现MongoDB的ACID事务,确保数据一致性。

我们会基于mydb数据库和users集合,创建一个Spring Boot应用,提供用户管理的REST API。本阶段约5000字,包含详细代码示例和生活化比喻,确保零基础的你能轻松上手!

第五阶段:Java集成实践

一、Spring Data MongoDB

Spring Data MongoDB是Spring框架的模块,简化了MongoDB的开发。它提供了Repository模式,让你用简单的接口定义就能实现CRUD操作,还支持自定义查询和审计功能。想象Spring Data MongoDB像一个“智能助手”,帮你把MongoDB的复杂操作变成几行代码。

我们将创建一个Spring Boot项目,实现用户管理的增删改查功能。

1. 项目初始化

步骤1:创建Spring Boot项目

  1. 访问start.spring.io

  2. 配置:

    • Project:Maven
    • Language:Java
    • Spring Boot:3.x(最新稳定版,截至2025年5月)
    • Group:com.example
    • Artifact:user-management
    • Dependencies:Spring Web、Spring Data MongoDB
  3. 下载项目,导入IntelliJ IDEA或Eclipse。

步骤2:配置MongoDB连接
编辑src/main/resources/application.properties

spring.data.mongodb.uri=mongodb://localhost:27017/mydb
spring.data.mongodb.database=mydb

步骤3:添加依赖
确保pom.xml包含以下依赖:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-mongodb</artifactId>
    </dependency>
</dependencies>
2. Repository模式实现

Repository模式是Spring Data的核心,通过定义接口,自动生成CRUD操作。我们将创建一个UserRepository来管理users集合。

定义User实体
创建src/main/java/com/example/usermanagement/model/User.java

package com.example.usermanagement.model;

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

import java.util.List;

@Document(collection = "users")
public class User {
    @Id
    private String id;
    private String name;
    private int age;
    private String city;
    private List<String> hobbies;

    // 构造函数
    public User() {}
    public User(String name, int age, String city, List<String> hobbies) {
        this.name = name;
        this.age = age;
        this.city = city;
        this.hobbies = hobbies;
    }

    // Getter和Setter
    public String getId() { return id; }
    public void setId(String id) { this.id = id; }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public int getAge() { return age; }
    public void setAge(int age) { this.age = age; }
    public String getCity() { return city; }
    public void setCity(String city) { this.city = city; }
    public List<String> getHobbies() { return hobbies; }
    public void setHobbies(List<String> hobbies) { this.hobbies = hobbies; }
}

创建Repository
创建src/main/java/com/example/usermanagement/repository/UserRepository.java

package com.example.usermanagement.repository;

import com.example.usermanagement.model.User;
import org.springframework.data.mongodb.repository.MongoRepository;

public interface UserRepository extends MongoRepository<User, String> {
}

说明

  • @Document(collection = "users"):指定MongoDB集合。
  • @Id:标记主键字段。
  • MongoRepository<User, String>:提供CRUD方法,User是实体类,Stringid类型。

测试Repository
创建一个REST控制器,测试CRUD操作。创建src/main/java/com/example/usermanagement/controller/UserController.java

package com.example.usermanagement.controller;

import com.example.usermanagement.model.User;
import com.example.usermanagement.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.Optional;

@RestController
@RequestMapping("/users")
public class UserController {
    @Autowired
    private UserRepository userRepository;

    // 创建用户
    @PostMapping
    public User createUser(@RequestBody User user) {
        return userRepository.save(user);
    }

    // 查询所有用户
    @GetMapping
    public List<User> getAllUsers() {
        return userRepository.findAll();
    }

    // 查询单个用户
    @GetMapping("/{id}")
    public Optional<User> getUserById(@PathVariable String id) {
        return userRepository.findById(id);
    }

    // 更新用户
    @PutMapping("/{id}")
    public User updateUser(@PathVariable String id, @RequestBody User user) {
        user.setId(id);
        return userRepository.save(user);
    }

    // 删除用户
    @DeleteMapping("/{id}")
    public void deleteUser(@PathVariable String id) {
        userRepository.deleteById(id);
    }
}

运行项目

  1. 确保MongoDB服务运行(mongod)。

  2. 运行Spring Boot应用(mvn spring-boot:run或通过IDE)。

  3. 使用Postman或浏览器测试API:

    • POST http://localhost:8080/users:创建用户(Body:{"name": "小明", "age": 20, "city": "北京", "hobbies": ["读书"]}
    • GET http://localhost:8080/users:查询所有用户
    • GET http://localhost:8080/users/{id}:查询单个用户
    • PUT http://localhost:8080/users/{id}:更新用户
    • DELETE http://localhost:8080/users/{id}:删除用户

生活化比喻:Repository像一个“智能文件柜”,你只要告诉它要存、取、改、删哪个用户,它就自动帮你搞定。

3. 自定义查询方法

Spring Data MongoDB支持通过方法名定义查询,自动生成MongoDB查询语句。我们将为UserRepository添加自定义查询方法。

修改UserRepository

package com.example.usermanagement.repository;

import com.example.usermanagement.model.User;
import org.springframework.data.mongodb.repository.MongoRepository;

import java.util.List;

public interface UserRepository extends MongoRepository<User, String> {
    // 查找指定城市的用户
    List<User> findByCity(String city);

    // 查找年龄大于某个值的用户
    List<User> findByAgeGreaterThan(int age);

    // 查找指定城市且年龄大于某个值的用户
    List<User> findByCityAndAgeGreaterThan(String city, int age);

    // 查找hobbies包含某项的用户
    List<User> findByHobbiesContaining(String hobby);
}

添加控制器方法
UserController中添加:

// 按城市查询
@GetMapping("/city/{city}")
public List<User> getUsersByCity(@PathVariable String city) {
    return userRepository.findByCity(city);
}

// 按年龄查询
@GetMapping("/age/{age}")
public List<User> getUsersByAgeGreaterThan(@PathVariable int age) {
    return userRepository.findByAgeGreaterThan(age);
}

// 按城市和年龄查询
@GetMapping("/city/{city}/age/{age}")
public List<User> getUsersByCityAndAge(@PathVariable String city, @PathVariable int age) {
    return userRepository.findByCityAndAgeGreaterThan(city, age);
}

// 按兴趣查询
@GetMapping("/hobby/{hobby}")
public List<User> getUsersByHobby(@PathVariable String hobby) {
    return userRepository.findByHobbiesContaining(hobby);
}

测试自定义查询

  • GET http://localhost:8080/users/city/北京:查找北京的用户
  • GET http://localhost:8080/users/age/20:查找年龄大于20的用户
  • GET http://localhost:8080/users/city/上海/age/20:查找上海且年龄大于20的用户
  • GET http://localhost:8080/users/hobby/读书:查找兴趣包含“读书”的用户

说明

  • 方法名如findByCity会映射为{"city": ?}查询。
  • findByCityAndAgeGreaterThan映射为{"city": ?, "age": {"$gt": ?}}
  • findByHobbiesContaining映射为数组查询。

生活化比喻:自定义查询方法像告诉“智能文件柜”:“帮我找北京的用户”或“找喜欢读书的人”,它自动翻译成MongoDB查询。

4. 审计功能(@CreatedDate等)

审计功能让MongoDB自动记录文档的创建时间、更新时间等信息,适合跟踪数据变更。我们将为User添加创建时间和更新时间。

启用审计
在主应用类上添加@EnableMongoAuditing

package com.example.usermanagement;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.mongodb.config.EnableMongoAuditing;

@SpringBootApplication
@EnableMongoAuditing
public class UserManagementApplication {
    public static void main(String[] args) {
        SpringApplication.run(UserManagementApplication.class, args);
    }
}

修改User实体
添加审计字段:

package com.example.usermanagement.model;

import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.mongodb.core.mapping.Document;

import java.time.LocalDateTime;
import java.util.List;

@Document(collection = "users")
public class User {
    @Id
    private String id;
    private String name;
    private int age;
    private String city;
    private List<String> hobbies;
    @CreatedDate
    private LocalDateTime createdDate;
    @LastModifiedDate
    private LocalDateTime lastModifiedDate;

    // 构造函数
    public User() {}
    public User(String name, int age, String city, List<String> hobbies) {
        this.name = name;
        this.age = age;
        this.city = city;
        this.hobbies = hobbies;
    }

    // Getter和Setter
    public String getId() { return id; }
    public void setId(String id) { this.id = id; }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public int getAge() { return age; }
    public void setAge(int age) { this.age = age; }
    public String getCity() { return city; }
    public void setCity(String city) { this.city = city; }
    public List<String> getHobbies() { return hobbies; }
    public void setHobbies(List<String> hobbies) { this.hobbies = hobbies; }
    public LocalDateTime getCreatedDate() { return createdDate; }
    public void setCreatedDate(LocalDateTime createdDate) { this.createdDate = createdDate; }
    public LocalDateTime getLastModifiedDate() { return lastModifiedDate; }
    public void setLastModifiedDate(LocalDateTime lastModifiedDate) { this.lastModifiedDate = lastModifiedDate; }
}

测试审计

  1. POST http://localhost:8080/users创建用户,MongoDB会自动设置createdDate
  2. PUT http://localhost:8080/users/{id}更新用户,lastModifiedDate会更新。
  3. GET http://localhost:8080/users/{id}查看用户,确认时间字段。

生活化比喻:审计功能像在便签上自动盖上“创建时间”和“最后修改时间”戳,帮你记录文件历史。

二、事务管理

MongoDB 4.0+支持ACID事务,确保多文档操作的原子性、一致性、隔离性和持久性。Spring Data MongoDB通过@Transactional简化事务管理。我们将实现一个事务示例,确保用户和相关日志同时保存。

1. ACID事务实现

ACID事务保证多个操作要么全部成功,要么全部回滚。MongoDB的事务需要运行在副本集(Replica Set)分片集群上。以下是为开发环境配置副本集的简单步骤:

配置副本集(单节点测试):

  1. 停止当前MongoDB服务。

  2. 创建数据目录(比如C:\data\db1/data/db1)。

  3. 启动MongoDB:

    mongod --port 27017 --dbpath /data/db1 --replSet rs0
    
  4. 连接MongoDB(mongosh),初始化副本集:

    rs.initiate()
    
  5. 确认副本集状态:

    rs.status()
    

修改Spring Boot配置
更新application.properties,连接副本集:

spring.data.mongodb.uri=mongodb://localhost:27017/mydb?replicaSet=rs0
2. 多文档事务Java示例

我们将创建一个服务,保存用户并记录操作日志,确保两者在事务中一致。

创建日志实体
创建src/main/java/com/example/usermanagement/model/LogEntry.java

package com.example.usermanagement.model;

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

import java.time.LocalDateTime;

@Document(collection = "logs")
public class LogEntry {
    @Id
    private String id;
    private String operation;
    private String userId;
    private LocalDateTime timestamp;

    public LogEntry() {}
    public LogEntry(String operation, String userId, LocalDateTime timestamp) {
        this.operation = operation;
        this.userId = userId;
        this.timestamp = timestamp;
    }

    // Getter和Setter
    public String getId() { return id; }
    public void setId(String id) { this.id = id; }
    public String getOperation() { return operation; }
    public void setOperation(String operation) { this.operation = operation; }
    public String getUserId() { return userId; }
    public void setUserId(String userId) { this.userId = userId; }
    public LocalDateTime getTimestamp() { return timestamp; }
    public void setTimestamp(LocalDateTime timestamp) { this.timestamp = timestamp; }
}

创建日志Repository
创建src/main/java/com/example/usermanagement/repository/LogRepository.java

package com.example.usermanagement.repository;

import com.example.usermanagement.model.LogEntry;
import org.springframework.data.mongodb.repository.MongoRepository;

public interface LogRepository extends MongoRepository<LogEntry, String> {
}

创建服务层
创建src/main/java/com/example/usermanagement/service/UserService.java

package com.example.usermanagement.service;

import com.example.usermanagement.model.LogEntry;
import com.example.usermanagement.model.User;
import com.example.usermanagement.repository.LogRepository;
import com.example.usermanagement.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;

@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;
    @Autowired
    private LogRepository logRepository;

    @Transactional
    public User createUserWithLog(User user) {
        // 保存用户
        User savedUser = userRepository.save(user);

        // 保存日志
        LogEntry log = new LogEntry("CREATE_USER", savedUser.getId(), LocalDateTime.now());
        logRepository.save(log);

        // 模拟错误,测试事务回滚
        if (user.getName().equals("error")) {
            throw new RuntimeException("Simulated error");
        }

        return savedUser;
    }
}

修改控制器
UserController中调用服务:

@Autowired
private UserService userService;

@PostMapping("/with-log")
public User createUserWithLog(@RequestBody User user) {
    return userService.createUserWithLog(user);
}

测试事务

  1. POST http://localhost:8080/users/with-log

    • Body:{"name": "小明", "age": 20, "city": "北京", "hobbies": ["读书"]}
    • 成功:用户和日志都保存。
  2. POST http://localhost:8080/users/with-log

    • Body:{"name": "error", "age": 20, "city": "北京", "hobbies": ["读书"]}
    • 失败:事务回滚,用户和日志都不保存。

说明

  • @Transactional确保用户和日志操作在同一事务中。
  • 如果抛出异常(如RuntimeException),MongoDB回滚所有操作。

生活化比喻:事务像一次“原子操作”,要么把用户便签和日志便签都放进文件夹,要么一张都不放,保证文件夹始终整齐。


总结与回顾

恭喜你完成了MongoDB的全部五阶段学习!通过本阶段,你:

  • 学会了用Spring Data MongoDB实现Repository模式和自定义查询。
  • 启用了审计功能,自动记录时间戳。
  • 实现了MongoDB的ACID事务,确保数据一致性。
  • 构建了一个简单的用户管理系统,整合了前四阶段的CRUD、查询、索引等知识。

你的MongoDB“文件夹”现在不仅灵活,还高效、可靠!以下是整个教程的回顾:

  1. 第一阶段:了解MongoDB基础、NoSQL、BSON,搭建环境。
  2. 第二阶段:掌握CRUD操作,用Java驱动实现增删改查。
  3. 第三阶段:学习复杂查询(条件、逻辑、数组)和聚合框架。
  4. 第四阶段:优化性能,创建索引,分析执行计划。
  5. 第五阶段:整合Spring Boot,开发用户管理系统。

接下来做什么?

  • 扩展项目:为用户管理系统添加更多功能,如分页查询、用户认证。
  • 深入学习:探索MongoDB的分布式部署、分片、备份恢复。
  • 实战应用:将MongoDB应用到自己的项目中,比如博客、电商平台。

你已经从MongoDB新手成长为有实战能力的开发者!继续探索,未来可期!