Introduction to Spring Data MongoDB – Spring Data MongoDB简介

最后修改: 2015年 8月 2日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

This article will be a quick and practical introduction to Spring Data MongoDB.

本文将对Spring Data MongoDB进行快速而实用的介绍。

We’ll go over the basics using both the MongoTemplate as well as MongoRepository, with practical examples to illustrate each operation.

我们将使用MongoTemplate以及MongoRepository来介绍基础知识,并通过实际例子来说明每项操作。

2. MongoTemplate and MongoRepository

2.MongoTemplateMongoRepository

The MongoTemplate follows the standard template pattern in Spring and provides a ready-to-go, basic API to the underlying persistence engine.

MongoTemplate遵循Spring中的标准模板模式,并为底层持久化引擎提供了现成的基本API。

The repository follows the Spring Data-centric approach and comes with more flexible and complex API operations, based on the well-known access patterns in all Spring Data projects.

资源库遵循以Spring Data为中心的方法,基于所有Spring Data项目中众所周知的访问模式,配备了更加灵活和复杂的API操作。

For both, we need to start by defining the dependency — for example, in the pom.xml, with Maven:

对于这两种情况,我们需要先定义依赖关系–例如,在pom.xml中,用Maven。

<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-mongodb</artifactId>
    <version>3.0.3.RELEASE</version>
</dependency>

To check if any new version of the library has been released, track the releases here.

要检查是否有任何新版本的库被发布,跟踪这里的发布情况

3. Configuration for MongoTemplate

3.为MongoTemplate配置

3.1. XML Configuration

3.1.XML配置

Let’s start with the simple XML configuration for the Mongo template:

让我们从Mongo模板的简单XML配置开始。

<mongo:mongo-client id="mongoClient" host="localhost" />
<mongo:db-factory id="mongoDbFactory" dbname="test" mongo-client-ref="mongoClient" />

We first need to define the factory bean responsible for creating Mongo instances.

我们首先需要定义负责创建Mongo实例的工厂Bean。

Next, we need to actually define (and configure) the template bean:

接下来,我们需要实际定义(和配置)模板Bean。

<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate"> 
    <constructor-arg ref="mongoDbFactory"/> 
</bean>

And finally, we need to define a post processor to translate any MongoExceptions thrown in @Repository annotated classes:

最后,我们需要定义一个后处理器来翻译@Repository注解的类中抛出的任何MongoExceptions

<bean class=
  "org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>

3.2. Java Configuration

3.2. Java配置

Let’s now create a similar configuration using Java config by extending the base class for MongoDB configuration AbstractMongoConfiguration:

现在让我们通过扩展MongoDB配置的基类AbstractMongoConfiguration,使用Java config创建一个类似的配置。

@Configuration
public class MongoConfig extends AbstractMongoClientConfiguration {
 
    @Override
    protected String getDatabaseName() {
        return "test";
    }
 
    @Override
    public MongoClient mongoClient() {
        ConnectionString connectionString = new ConnectionString("mongodb://localhost:27017/test");
        MongoClientSettings mongoClientSettings = MongoClientSettings.builder()
            .applyConnectionString(connectionString)
            .build();
        
        return MongoClients.create(mongoClientSettings);
    }
 
    @Override
    public Collection getMappingBasePackages() {
        return Collections.singleton("com.baeldung");
    }
}

Note that we didn’t need to define MongoTemplate bean in the previous configuration since it’s already defined in AbstractMongoClientConfiguration.

请注意,我们不需要在之前的配置中定义MongoTemplate Bean,因为它已经在AbstractMongoClientConfiguration中定义。

We can also use our configuration from scratch without extending AbstractMongoClientConfiguration:

我们也可以从头开始使用我们的配置,而无需扩展AbstractMongoClientConfiguration

@Configuration
public class SimpleMongoConfig {
 
    @Bean
    public MongoClient mongo() {
        ConnectionString connectionString = new ConnectionString("mongodb://localhost:27017/test");
        MongoClientSettings mongoClientSettings = MongoClientSettings.builder()
          .applyConnectionString(connectionString)
          .build();
        
        return MongoClients.create(mongoClientSettings);
    }

    @Bean
    public MongoTemplate mongoTemplate() throws Exception {
        return new MongoTemplate(mongo(), "test");
    }
}

4. Configuration for MongoRepository

4.MongoRepository的配置

4.1. XML Configuration

4.1.XML配置

To make use of custom repositories (extending the MongoRepository), we need to continue the configuration from section 3.1. and set up the repositories:

为了利用自定义仓库(扩展MongoRepository),我们需要继续3.1节的配置,并设置仓库。

<mongo:repositories 
  base-package="com.baeldung.repository" mongo-template-ref="mongoTemplate"/>

4.2. Java Configuration

4.2. Java配置

Similarly, we’ll build on the configuration we already created in section 3.2. and add a new annotation into the mix:

同样,我们将在第3.2节中已经创建的配置的基础上,将一个新的注释添加到组合中。

@EnableMongoRepositories(basePackages = "com.baeldung.repository")

4.3. Create the Repository

4.3.创建存储库

After the configuration, we need to create a repository — extending the existing MongoRepository interface:

配置之后,我们需要创建一个存储库–扩展现有的MongoRepository接口。

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

Now we can auto-wire this UserRepository and use operations from MongoRepository or add custom operations.

现在我们可以自动连接这个UserRepository,并使用MongoRepository的操作或添加自定义操作。

5. Using MongoTemplate

5.使用MongoTemplate

5.1. Insert

5.1.插入

Let’s start with the insert operation as well as an empty database:

让我们从插入操作以及一个空数据库开始。

{
}

Now if we insert a new user:

现在,如果我们插入一个新的用户。

User user = new User();
user.setName("Jon");
mongoTemplate.insert(user, "user");

the database will look like this:

数据库将看起来像这样。

{
    "_id" : ObjectId("55b4fda5830b550a8c2ca25a"),
    "_class" : "com.baeldung.model.User",
    "name" : "Jon"
}

5.2. Save – Insert

5.2.保存-插入

The save operation has save-or-update semantics: if an id is present, it performs an update, and if not, it does an insert.

保存操作具有保存或更新的语义:如果有一个id存在,它执行更新,如果没有,它进行插入。

Let’s look at the first semantic — the insert.

让我们来看看第一个语义–插入。

Here’s the initial state of the database:

下面是数据库的初始状态

{
}

When we now save a new user:

当我们现在save一个新的用户。

User user = new User();
user.setName("Albert"); 
mongoTemplate.save(user, "user");

the entity will be inserted in the database:

实体将被插入到数据库中。

{
    "_id" : ObjectId("55b52bb7830b8c9b544b6ad5"),
    "_class" : "com.baeldung.model.User",
    "name" : "Albert"
}

Next, we’ll look at the same operation — save — with update semantics.

接下来,我们来看看同样的操作–save–的更新语义。

5.3. Save – Update

5.3.保存-更新

Let’s now look at save with update semantics, operating on an existing entity:

现在让我们看看具有更新语义的save,对现有实体进行操作。

{
    "_id" : ObjectId("55b52bb7830b8c9b544b6ad5"),
    "_class" : "com.baeldung.model.User",
    "name" : "Jack"
}

When we save the existing user, we will update it:

当我们save现有用户时,我们将更新它。

user = mongoTemplate.findOne(
  Query.query(Criteria.where("name").is("Jack")), User.class);
user.setName("Jim");
mongoTemplate.save(user, "user");

The database will look like this:

该数据库将看起来像这样。

{
    "_id" : ObjectId("55b52bb7830b8c9b544b6ad5"),
    "_class" : "com.baeldung.model.User",
    "name" : "Jim"
}

We can see that in this particular example, save uses the semantics of update because we use an object with given _id.

我们可以看到,在这个特殊的例子中,save使用了update的语义,因为我们使用了一个具有给定_id的对象。

5.4. UpdateFirst

5.4.UpdateFirst

updateFirst updates the very first document that matches the query.

updateFirst更新第一个匹配查询的文档。

Let’s start with the initial state of the database:

让我们从数据库的初始状态开始。

[
    {
        "_id" : ObjectId("55b5ffa5511fee0e45ed614b"),
        "_class" : "com.baeldung.model.User",
        "name" : "Alex"
    },
    {
        "_id" : ObjectId("55b5ffa5511fee0e45ed614c"),
        "_class" : "com.baeldung.model.User",
        "name" : "Alex"
    }
]

When we now run the updateFirst:

当我们现在运行updateFirst

Query query = new Query();
query.addCriteria(Criteria.where("name").is("Alex"));
Update update = new Update();
update.set("name", "James");
mongoTemplate.updateFirst(query, update, User.class);

only the first entry will be updated:

只有第一个条目会被更新。

[
    {
        "_id" : ObjectId("55b5ffa5511fee0e45ed614b"),
        "_class" : "com.baeldung.model.User",
        "name" : "James"
    },
    {
        "_id" : ObjectId("55b5ffa5511fee0e45ed614c"),
        "_class" : "com.baeldung.model.User",
        "name" : "Alex"
    }
]

5.5. UpdateMulti

5.5.UpdateMulti

UpdateMulti updates all documents that match the given query.

UpdateMulti 更新所有符合给定查询的文件。

First, here’s the state of the database before doing the updateMulti:

首先,这是进行updateMulti之前的数据库状态。

[
    {
        "_id" : ObjectId("55b5ffa5511fee0e45ed614b"),
        "_class" : "com.baeldung.model.User",
        "name" : "Eugen"
    },
    {
        "_id" : ObjectId("55b5ffa5511fee0e45ed614c"),
        "_class" : "com.baeldung.model.User",
        "name" : "Eugen"
    }
]

Now let’s run the updateMulti operation:

现在让我们运行updateMulti操作。

Query query = new Query();
query.addCriteria(Criteria.where("name").is("Eugen"));
Update update = new Update();
update.set("name", "Victor");
mongoTemplate.updateMulti(query, update, User.class);

Both existing objects will be updated in the database:

两个现有的对象都将在数据库中被更新。

[
    {
        "_id" : ObjectId("55b5ffa5511fee0e45ed614b"),
        "_class" : "com.baeldung.model.User",
        "name" : "Victor"
    },
    {
        "_id" : ObjectId("55b5ffa5511fee0e45ed614c"),
        "_class" : "com.baeldung.model.User",
        "name" : "Victor"
    }
]

5.6. FindAndModify

5.6.查找和修改

This operation works like updateMulti, but it returns the object before it was modified.

这个操作的工作原理与updateMulti类似,但它返回对象被修改之前的结果。

First, this is the state of the database before calling findAndModify:

首先,这是调用findAndModify之前数据库的状态。

{
    "_id" : ObjectId("55b5ffa5511fee0e45ed614b"),
    "_class" : "com.baeldung.model.User",
    "name" : "Markus"
}

Let’s look at the actual operation code:

让我们来看看实际的操作代码。

Query query = new Query();
query.addCriteria(Criteria.where("name").is("Markus"));
Update update = new Update();
update.set("name", "Nick");
User user = mongoTemplate.findAndModify(query, update, User.class);

The returned user object has the same values as the initial state in the database.

返回的用户对象的值与数据库中的初始状态相同。

However, this is the new state in the database:

然而,这就是数据库中的新状态。

{
    "_id" : ObjectId("55b5ffa5511fee0e45ed614b"),
    "_class" : "com.baeldung.model.User",
    "name" : "Nick"
}

5.7. Upsert

5.7.Upsert

The upsert works on the find and modify else create semantics: if the document is matched, update it, or else create a new document by combining the query and update object.

upsert查找和修改否则创建语义上工作:如果文档被匹配,则更新它,否则通过结合查询和更新对象创建一个新文档。

Let’s start with the initial state of the database:

让我们从数据库的初始状态开始。

{
    "_id" : ObjectId("55b5ffa5511fee0e45ed614b"),
    "_class" : "com.baeldung.model.User",
    "name" : "Markus"
}

Now let’s run the upsert:

现在我们来运行upsert

Query query = new Query();
query.addCriteria(Criteria.where("name").is("Markus"));
Update update = new Update();
update.set("name", "Nick");
mongoTemplate.upsert(query, update, User.class);

Here’s the state of the database after the operation:

下面是操作后数据库的状态。

{
    "_id" : ObjectId("55b5ffa5511fee0e45ed614b"),
    "_class" : "com.baeldung.model.User",
    "name" : "Nick"
}

5.8. Remove

5.8.移除

We’ll look at the state of the database before calling remove:

在调用remove之前,我们要看一下数据库的状态。

{
    "_id" : ObjectId("55b5ffa5511fee0e45ed614b"),
    "_class" : "com.baeldung.model.User",
    "name" : "Benn"
}

Let’s now run remove:

现在让我们运行remove

mongoTemplate.remove(user, "user");

The result will be as expected:

结果将如期而至。

{
}

6. Using MongoRepository

6.使用MongoRepository

6.1. Insert

6.1.插入

First, we’ll see the state of the database before running the insert:

首先,我们来看看运行insert之前数据库的状态。

{
}

Now we’ll insert a new user:

现在我们要插入一个新的用户。

User user = new User();
user.setName("Jon");
userRepository.insert(user);

And here’s the end state of the database:

而这里是数据库的最终状态。

{
    "_id" : ObjectId("55b4fda5830b550a8c2ca25a"),
    "_class" : "com.baeldung.model.User",
    "name" : "Jon"
}

Note how the operation works the same as the insert in the MongoTemplate API.

注意该操作的工作原理与MongoTemplate API中的insert相同。

6.2. Save Insert

6.2.保存插入

Similarly, save works the same as the save operation in the MongoTemplate API.

同样,saveMongoTemplate API中的save操作工作相同。

Let’s start by looking at the insert semantics of the operation.

让我们先看看该操作的插入语义

Here’s the initial state of the database:

这里是数据库的初始状态。

{
}

Now we execute the save operation:

现在我们执行save操作。

User user = new User();
user.setName("Aaron");
userRepository.save(user);

This results in the user being added to the database:

这将导致该用户被添加到数据库中。

{
    "_id" : ObjectId("55b52bb7830b8c9b544b6ad5"),
    "_class" : "com.baeldung.model.User",
    "name" : "Aaron"
}

Note again how save works with insert semantics because we are inserting a new object.

再次注意save如何与insert语义一起工作,因为我们正在插入一个新对象。

6.3. Save Update

6.3.保存更新

Let’s now look at the same operation but with update semantics.

现在让我们看一下同样的操作,但有更新语义。

First, here’s the state of the database before running the new save:

首先,这是运行新的save之前的数据库状态。

{
    "_id" : ObjectId("55b52bb7830b8c9b544b6ad5"),
    "_class" : "com.baeldung.model.User",
    "name" : "Jack"81*6
}

Now we execute the operation:

现在我们执行这个操作。

user = mongoTemplate.findOne(
  Query.query(Criteria.where("name").is("Jack")), User.class);
user.setName("Jim");
userRepository.save(user);

Finally, here is the state of the database:

最后,这里是数据库的状态。

{
    "_id" : ObjectId("55b52bb7830b8c9b544b6ad5"),
    "_class" : "com.baeldung.model.User",
    "name" : "Jim"
}

Note again how save works with update semantics because we are using an existing object.

再次注意save是如何与update语义一起工作的,因为我们使用的是一个现有的对象。

6.4. Delete

6.4.删除

Here’s the state of the database before calling delete:

下面是调用delete之前的数据库状态。

{
    "_id" : ObjectId("55b5ffa5511fee0e45ed614b"),
    "_class" : "com.baeldung.model.User",
    "name" : "Benn"
}

Let’s run delete:

让我们运行delete

userRepository.delete(user);

And here’s our result:

这就是我们的结果。

{
}

6.5. FindOne

6.5.FindOne

Next, this is the state of the database when findOne is called:

接下来,这是调用findOne时数据库的状态。

{
    "_id" : ObjectId("55b5ffa5511fee0e45ed614b"),
    "_class" : "com.baeldung.model.User",
    "name" : "Chris"
}

Let’s now execute the findOne:

现在我们来执行findOne

userRepository.findOne(user.getId())

And the result will return the existing data:

而结果将返回现有的数据。

{
    "_id" : ObjectId("55b5ffa5511fee0e45ed614b"),
    "_class" : "com.baeldung.model.User",
    "name" : "Chris"
}

6.6. Exists

6.6.存在

The state of the database before calling exists:

调用exists之前的数据库状态。

{
    "_id" : ObjectId("55b5ffa5511fee0e45ed614b"),
    "_class" : "com.baeldung.model.User",
    "name" : "Harris"
}

Now let’s run exists, which will of course return true:

现在让我们运行exists,它当然会返回true

boolean isExists = userRepository.exists(user.getId());

6.7. FindAll With Sort

6.7.FindAllWith Sort

The state of the database before calling findAll:

调用findAll之前的数据库状态。

[
    {
        "_id" : ObjectId("55b5ffa5511fee0e45ed614b"),
        "_class" : "com.baeldung.model.User",
        "name" : "Brendan"
    },
    {
       "_id" : ObjectId("67b5ffa5511fee0e45ed614b"),
       "_class" : "com.baeldung.model.User",
       "name" : "Adam"
    }
]

Let’s now run findAll with Sort:

现在让我们运行findAll排序

List<User> users = userRepository.findAll(Sort.by(Sort.Direction.ASC, "name"));

The result will be sorted by name in ascending order:

结果将是按名字升序排序

[
    {
        "_id" : ObjectId("67b5ffa5511fee0e45ed614b"),
        "_class" : "com.baeldung.model.User",
        "name" : "Adam"
    },
    {
        "_id" : ObjectId("55b5ffa5511fee0e45ed614b"),
        "_class" : "com.baeldung.model.User",
        "name" : "Brendan"
    }
]

6.8. FindAll With Pageable

6.8.FindAllWithPageable

The state of the database before calling findAll:

调用findAll之前的数据库状态。

[
    {
        "_id" : ObjectId("55b5ffa5511fee0e45ed614b"),
        "_class" : "com.baeldung.model.User",
        "name" : "Brendan"
    },
    {
        "_id" : ObjectId("67b5ffa5511fee0e45ed614b"),
        "_class" : "com.baeldung.model.User",
        "name" : "Adam"
    }
]

Let’s now execute findAll with a pagination request:

现在让我们用一个分页请求来执行findAll

Pageable pageableRequest = PageRequest.of(0, 1);
Page<User> page = userRepository.findAll(pageableRequest);
List<User> users = pages.getContent();

The resulting users list will be only one user:

产生的users列表将只有一个用户:

{
    "_id" : ObjectId("55b5ffa5511fee0e45ed614b"),
    "_class" : "com.baeldung.model.User",
    "name" : "Brendan"
}

7. Annotations

7.注解

Finally, let’s also go over the simple annotations that Spring Data uses to drive these API operations.

最后,我们也来看看Spring Data用来驱动这些API操作的简单注释。

The field level @Id annotation can decorate any type, including long and string:

字段级@Id注解可以装饰任何类型,包括longstring

@Id
private String id;

If the value of the @Id field is not null, it’s stored in the database as-is; otherwise, the converter will assume we want to store an ObjectId in the database (either ObjectId, String or BigInteger work).

如果@Id字段的值不是空的,它就会按原样存储在数据库中;否则,转换器就会认为我们要在数据库中存储一个ObjectId(无论是ObjectIdString还是BigInteger工作)。

We’ll next look at @Document:

我们接下来看看@Document

@Document
public class User {
    //
}

This annotation simply marks a class as being a domain object that needs to be persisted to the database, along with allowing us to choose the name of the collection to be used.

这个注解只是将一个类标记为需要持久化到数据库的领域对象,同时允许我们选择要使用的集合的名称。

8. Conclusion

8.结论

This article was a quick but comprehensive introduction to using MongoDB with Spring Data, both via the MongoTemplate API as well as making use of MongoRepository.

本文快速但全面地介绍了在Spring Data中使用MongoDB的情况,包括通过MongoTemplate API以及利用MongoRepository

The implementation of all these examples and code snippets can be found over on GitHub.

所有这些例子和代码片段的实现可以在GitHub上找到over