如何使用 Spring Data MongoDB 访问 MongoDB 数据库?

Spring Data MongoDB 是 Spring 框架提供的一个访问 MongoDB 数据库的模块,该模块延续了 Spring Data 系列统一的数据库访问风格(通过 Template 的方式与定义 Repository 接口的方式),借助于该模块可以使 MongoDB 的访问变得简单又高效。

本文以一个使用 Maven 管理的 Spring Boot 工程为例,结合本地搭建的 MongoDB(版本为 7.0.7)来演示 Spring Data MongoDB 的使用。

在 Spring Boot 中使用 Spring Data MongoDB,只需要引入一个 spring-boot-starter-data-mongodb 依赖即可,该依赖会自动将 Spring Data MongoDB 及相关依赖一并引入,并已与 Spring Boot 进行了无缝集成。

如下为示例工程所使用的 JDK、Maven、Spring Boot 与 Spring Data MongoDB 的版本:

JDK:Amazon Corretto 17.0.8
Maven:3.9.2
Spring Boot:3.2.4
Spring Data MongoDB:4.2.4

本文将以 User 的增、删、改、查为例来演示 Spring Data MongoDB 的使用。开始前先让我们准备一下测试数据。

1 测试数据准备

使用 MongoShell 连接本地 MongoDB 数据库 mongodb://localhost:27017。然后在 MongoShell 命令行执行如下语句来创建一个测试数据库 test,并在 test 库里创建一个集合 users,最后在 users 集合插入 3 条测试数据。

use test

db.createCollection("users")

db.getCollection("users").insertMany(
  [
    {
      email: "larry@larry.com",
      name: "Larry",
      role: "ADMIN",
      description: "I am Larry",
      created_at: ISODate("2024-01-01T08:00:00+08:00"),
      updated_at: ISODate("2023-01-01T08:00:00+08:00"),
      deleted: false
    },
    {
      email: "jacky@jacky.com",
      name: "Jacky",
      role: "EDITOR",
      description: "I am Jacky",
      created_at: ISODate("2024-02-01T08:00:00+08:00"),
      updated_at: ISODate("2023-02-01T08:00:00+08:00"),
      deleted: false
    },
    {
      email: "lucy@lucy.com",
      name: "Lucy",
      role: "VIEWER",
      description: "I am Lucy",
      created_at: ISODate("2024-03-01T08:00:00+08:00"),
      updated_at: ISODate("2023-03-01T08:00:00+08:00"),
      deleted: false
    }
  ]
)

查询一下,发现 3 条数据均已插入,且自动生成了 ID。

db.getCollection("users").find({})

[
  {
    _id: ObjectId('6607d1e438537258779f990a'),
    email: 'larry@larry.com',
    name: 'Larry',
    role: 'ADMIN',
    description: 'I am Larry',
    created_at: ISODate('2024-01-01T00:00:00.000Z'),
    updated_at: ISODate('2023-01-01T00:00:00.000Z'),
    deleted: false
  },
  {
    _id: ObjectId('6607d1e438537258779f990b'),
    email: 'jacky@jacky.com',
    name: 'Jacky',
    role: 'EDITOR',
    description: 'I am Jacky',
    created_at: ISODate('2024-02-01T00:00:00.000Z'),
    updated_at: ISODate('2023-02-01T00:00:00.000Z'),
    deleted: false
  },
  {
    _id: ObjectId('6607d1e438537258779f990c'),
    email: 'lucy@lucy.com',
    name: 'Lucy',
    role: 'VIEWER',
    description: 'I am Lucy',
    created_at: ISODate('2024-03-01T00:00:00.000Z'),
    updated_at: ISODate('2023-03-01T00:00:00.000Z'),
    deleted: false
  }
]

测试数据准备好后,下面看一下示例工程的依赖与配置。

2 示例工程依赖与配置

2.1 POM.xml 依赖项

如下为示例工程 spring-data-mongodb-demo 的根目录文件 pom.xml 的内容,可以看到其使用了 spring-boot-starter-parent,并引入了 3 个依赖项:spring-boot-starter-webspring-boot-starter-data-mongodblombok

<!-- pom.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.2.4</version>
        <relativePath/>
    </parent>

    <groupId>com.example</groupId>
    <artifactId>spring-data-mongodb-demo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <java.version>17</java.version>
    </properties>
    <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>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <!-- test -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

2.2 application.yaml 配置

如下为 application.yaml 文件的内容,可以看到该文件配置了本地 MongoDB 的连接信息,并开启了 MongoDB 查询语句的打印。

# src/main/resources/application.yaml
spring:
  data:
    mongodb:
      host: localhost
      port: 27017
      database: test

logging:
  level:
    org.springframework.data.mongodb.core: DEBUG

示例工程依赖和配置准备好后,即可以尝试对 Spring Data MongoDB 进行使用了。

3 开始使用 Spring Data MongoDB

3.1 编写 Model 类

首先需要编写一下 Model 类 User.java,其代码如下:

// src/main/java/com/example/demo/model/User.java
package com.example.demo.model;

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

import java.util.Date;

@Data
@Document("users")
public class User {

    @Id
    private String id;
    private String email;
    private String name;
    private Role role;
    private String description;
    @Field("created_at")
    private Date createdAt;
    @Field("updated_at")
    private Date updatedAt;
    private Boolean deleted;

    public enum Role {
        ADMIN,
        EDITOR,
        VIEWER
    }
}

可以看到,如上代码在类上使用了 @Document 注解,并指定了对应的 MongoDB 集合为 users;对主键字段使用了 @Id 注解;并对与 MongoDB 集合中命名不一致的属性使用了 @Field 注解指定了实际的字段名。此外还使用 Lombok 的 @Data 注解自动生成了 SettersGetters

3.2 编写 Repository

下面我们为 User Model 创建一个对应的 Repository 接口 UserRepository,并将其扩展 MongoRepository 接口。MongoRepository 接口已内置了常用的增、删、改、查方法,通常来说我们无需自己编写任何方法,使用这些默认方法就满足大多数需求了。但 Spring Data MongoDB 还支持在 Repository 接口中按照约定的命名规则添加自定义查询方法。

// src/main/java/com/example/demo/dao/UserRepository.java
package com.example.demo.dao;

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

import java.util.List;

public interface UserRepository extends MongoRepository<User, String> {

    List<User> findByName(String name);

}

可以看到,我们只在 UserRepository 接口增加了一个 findByName 自定义方法。

下面即在 src/test/java 文件夹下针对 UserRepository 编写一个单元测试类来对其提供的增、删、改、查方法进行测试。

// src/test/java/com/example/demo/dao/UserRepositoryTest.java
package com.example.demo.dao;

import com.example.demo.model.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

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

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

@SpringBootTest
public class UserRepositoryTest {

    @Autowired
    private UserRepository userRepository;

    @Test
    public void testCount() {
        long count = userRepository.count();

        assertEquals(3, count);
    }

    @Test
    public void testFindAll() {
        List<User> users = userRepository.findAll();

        assertEquals(3, users.size());
    }

    @Test
    public void testFindById() {
        Optional<User> optional = userRepository.findById("6607d1e438537258779f990a");

        assertTrue(optional.isPresent());
        assertEquals("Larry", optional.get().getName());
    }

    @Test
    public void testFindByName() {
        List<User> users = userRepository.findByName("Larry");

        assertEquals(1, users.size());
        assertEquals("Larry", users.get(0).getName());
    }

    @Test
    public void testSave() {
        Date now = new Date();

        User user = new User();
        user.setEmail("linda@linda.com");
        user.setName("Linda");
        user.setRole(User.Role.EDITOR);
        user.setDescription("I am Linda");
        user.setCreatedAt(now);
        user.setUpdatedAt(now);
        user.setDeleted(false);

        // save
        userRepository.save(user);
    }

    @Test
    public void testUpdate() {
        User user = userRepository.findById("6607d1e438537258779f990a").get();
        user.setName("Larry2");

        userRepository.save(user);
    }

    @Test
    public void testDelete() {
        userRepository.deleteById("6607d1e438537258779f990a");
    }

}

测试发现,包括自定义方法在内的各个增、删、改、查方法均是好用的。

3.3 使用 MongoTemplate

除了通过定义 Repository 接口来对 MongoDB 进行通用的增、删、改、查操作外,我们还可以使用更加灵活的 MongoTemplate 来对 MongoDB 进行操作。

下面即为 MongoTemplate 编写一个单元测试类来对其提供的功能进行简单测试。

// src/test/java/com/example/demo/dao/MongoTemplateTest.java
package com.example.demo.dao;

import com.example.demo.model.User;
import com.mongodb.client.result.UpdateResult;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;

import java.util.List;

import static org.junit.jupiter.api.Assertions.*;

@SpringBootTest
public class MongoTemplateTest {

    @Autowired
    private MongoTemplate mongoTemplate;

    @Test
    public void testFindAll() {
        List<User> users = mongoTemplate.findAll(User.class);

        assertFalse(users.isEmpty());
    }

    @Test
    public void testFindByName() {
        Query query = new Query(Criteria.where("name").is("Jacky"));
        User user = mongoTemplate.findOne(query, User.class);

        assertNotNull(user);
        assertEquals("jacky@jacky.com", user.getEmail());
    }

    @Test
    public void testUpdateEmailByName() {
        Query query = new Query(Criteria.where("name").is("Jacky"));
        Update update = new Update().set("email", "jacky2@jacky.com");

        UpdateResult result = mongoTemplate.updateMulti(query, update, User.class);
        assertEquals(1, result.getModifiedCount());
    }

}

可以看到,使用 MongoTemplate 时,我们可以新建一个 Query 对象来拼装任意复杂的查询条件,进而对 MongoDB 进行查询或更新。

综上,本文以示例工程的方式演示了 Spring Data MongoDB 提供的两种 MongoDB 的访问方式(通过定义 Repository 和使用 MongoTemplate),总体来说该模块保持了 Spring Data 系列通用的设计思路与实现模式,使用起来非常的直观简便。完整示例工程已提交至本人 GitHub,欢迎关注或 Fork。

参考资料

[1] Spring: Spring Data MongoDB Reference Document - https://docs.spring.io/spring-data/mongodb/reference/4.2.4/index.html

[2] MongoDB: Spring Boot Integration With MongoDB Tutorial - https://www.mongodb.com/compatibility/spring-boot

[3] DigitalOcean: Spring Boot MongoDB - https://www.digitalocean.com/community/tutorials/spring-boot-mongodb

评论

正在加载评论......