1. Overview
1.概述
In this tutorial, we’ll learn how to configure MongoDB collection names for our classes, along with a practical example. We’ll be using Spring Data, which gives us a few options to achieve this with little configuration. We’ll explore each option by building a simple music store. That way, we can find out when it makes sense to use them.
在本教程中,我们将学习如何为我们的类配置 MongoDB 集合名称,并结合一个实际的例子。我们将使用Spring Data,它为我们提供了几个选项来实现这一目标,只需进行少量配置。我们将通过建立一个简单的音乐商店来探索每个选项。这样,我们就可以发现何时使用它们是有意义的。
2. Use Case and Setup
2.用例和设置
Our use case has four simple classes: MusicAlbum, Compilation, MusicTrack, and Store. Each class will have its collection name configured in a different way. Also, each class will have its own MongoRepository. No custom queries will be needed. Moreover, we’ll need a properly configured instance of a MongoDB database.
我们的用例有四个简单的类。MusicAlbum, Compilation, MusicTrack, 和Store。每个类都将以不同的方式配置其集合名称。而且,每个类都将有自己的MongoRepository。将不需要自定义查询。此外,我们将需要一个正确配置的MongoDB数据库的实例。
2.1. Service to List Contents of a Collection by Name
2.1.按名称列出集合的内容的服务
Firstly, let’s write a controller to assert our configurations are working. We’ll do that by searching by collection name. Note that when using repositories, collection name configurations are transparent:
首先,让我们编写一个controller来断定我们的配置是否有效。注意,当使用资源库时,集合名称配置是透明的。
@RestController
@RequestMapping("/collection")
public class CollectionController {
@Autowired
private MongoTemplate mongoDb;
@GetMapping("/{name}")
public List<DBObject> get(@PathVariable String name) {
return mongoDb.findAll(DBObject.class, name);
}
}
This controller is based around MongoTemplate and uses the generic type DBObject, which doesn’t depend on classes and repositories. Also, this way, we’ll be able to see internal MongoDB properties. Most importantly, the “_class” property, used by Spring Data to unmarshal a JSON object, guarantees our configuration is correct.
该控制器围绕MongoTemplate,并使用通用类型DBObject,它不依赖于类和存储库。另外,这样一来,我们就能看到MongoDB的内部属性。最重要的是,Spring Data用来解读JSON对象的”_class “属性,可以保证我们的配置是正确的。
2.2. API Service
2.2.API服务
Secondly, we’ll start building our service, which just saves objects and retrieves their collection. The Compilation class will be created later on in our first configuration example:
其次,我们将开始构建我们的服务,它只是保存对象和检索其集合。Compilation类将在以后我们的第一个配置例子中创建。
@Service
public class MusicStoreService {
@Autowired
private CompilationRepository compilationRepository;
public Compilation add(Compilation item) {
return compilationRepository.save(item);
}
public List<Compilation> getCompilationList() {
return compilationRepository.findAll();
}
// other service methods
}
2.3. API Endpoints
2.3.API端点
Finally, let’s write a controller to interface with our application. We’ll expose endpoints for our service methods:
最后,让我们写一个控制器来与我们的应用程序对接。我们将为我们的服务方法暴露端点。
@RestController
@RequestMapping("/music")
public class MusicStoreController {
@Autowired
private MusicStoreService service;
@PostMapping("/compilation")
public Compilation post(@RequestBody Compilation item) {
return service.add(item);
}
@GetMapping("/compilation")
public List<Compilation> getCompilationList() {
return service.getCompilationList();
}
// other endpoint methods
}
After that, we’re ready to start configuring our classes.
之后,我们就可以开始配置我们的类了。
3. Configuration With @Document Annotation
3.使用@Document注释进行配置
Available since Spring Data version 1.9, the Document annotation does everything we’ll need. It’s specific for MongoDB but similar to JPA’s Entity annotation. As of this writing, there’s no way to define a naming strategy for collection names, only for field names. Therefore, let’s explore what’s available.
自Spring Data 1.9版本以来,Document注解可以完成我们需要的一切。它是针对 MongoDB 的,但与 JPA 的 Entity 注解相似。截至目前,没有办法为集合名称定义命名策略,只能为字段名称定义。因此,我们来探讨一下可用的方法。
3.1. Default Behavior
3.1.默认行为
The default behavior considers the collection name to be the same as the class name but starting with lower case. In short, we just need to add the Document annotation, and it’ll work:
默认行为认为集合名与类名相同,但以小写开头。简而言之,我们只需要添加Document注解,就可以了。
@Document
public class Compilation {
@Id
private String id;
// getters and setters
}
After that, all inserts from our Compilation repository will go into a collection named “compilation” in MongoDB:
之后,所有来自我们Compilation存储库的插入都将进入MongoDB中一个名为 “compilation “的集合。
$ curl -X POST http://localhost:8080/music/compilation -H 'Content-Type: application/json' -d '{
"name": "Spring Hits"
}'
{ "id": "6272e26e04a673360d926ca1" }
Let’s list the contents of our “compilation” collection to verify our configuration:
让我们列出我们的 “汇编 “集合的内容,以验证我们的配置。
$ curl http://localhost:8080/collection/compilation
[
{
"name": "Spring Hits",
"_class": "com.baeldung.boot.collection.name.data.Compilation"
}
]
And that’s the cleanest way to configure a collection name, as it’s basically the same as our class name. One disadvantage is that if we decide to change our database naming conventions, we’ll need to refactor all our classes. For instance, if we decide to use snake-case for our collection names, we won’t be able to take advantage of the default behavior.
而这是配置集合名称的最简洁的方式,因为它基本上与我们的类名相同。一个缺点是,如果我们决定改变我们的数据库命名惯例,我们将需要重构我们所有的类。例如,如果我们决定为我们的集合名称使用蛇形大小写,我们将不能利用默认行为。
3.2. Overriding the value Property
3.2.重写value属性
The Document annotation lets us override the default behavior with the collection property. Since this property is an alias for the value property, we can set it implicitly:
Document注解让我们用collection属性覆盖了默认行为。因为这个属性是value属性的别名,我们可以隐式地设置它。
@Document("albums")
public class MusicAlbum {
@Id
private String id;
private String name;
private String artist;
// getters and setters
}
Now, instead of documents going into a collection named “musicAlbum”, they’ll go to the “albums” collection. This is the simplest way to configure a collection name with Spring Data. To see it in action, let’s add an album to our collection:
现在,文件将进入名为 “musicAlbum “的集合,而不是进入 “assets “集合。这是用Spring Data配置集合名称的最简单方法。为了看到它的作用,让我们在我们的集合中添加一个专辑。
$ curl -X POST 'http://localhost:8080/music/album' -H 'Content-Type: application/json' -d '{
"name": "Album 1",
"artist": "Artist A"
}'
{ "id": "62740de003d2452a61a75c35" }
And then, we can fetch our “albums” collection, making sure our configuration works:
然后,我们可以获取我们的 “相册 “集合,确保我们的配置能够发挥作用。
$ curl 'http://localhost:8080/collection/albums'
[
{
"name": "Album 1",
"artist": "Artist A",
"_class": "com.baeldung.boot.collection.name.data.MusicAlbum"
}
]
Also, it’s great for adapting our application to an existing database, with collection names that don’t match our classes. One downside is that if we needed to add a default prefix, we’d need to do it for every class.
另外,它也很适合于使我们的应用程序适应现有的数据库,其集合名称与我们的类不匹配。一个缺点是,如果我们需要添加一个默认的前缀,我们需要为每一个类都这样做。
3.3. Using a Configuration Property With SpEL
3.3.使用SpEL的配置属性
This combination can help do things that are not possible using the Document annotation alone. We’ll start with an application-specific property that we want to reuse among our classes.
这种组合可以帮助做一些单独使用Document注解所不能做到的事情。我们将从一个特定于应用程序的属性开始,我们想在我们的类中重用这个属性。
First, let’s add a property to our application.properties that we’ll use as a suffix to our collection names:
首先,让我们在我们的application.properties中添加一个属性,我们将用它作为集合名称的后缀。
collection.suffix=db
Now, let’s reference it with SpEL via the environment bean to create our next class:
现在,让我们通过environment bean引用SpEL来创建我们的下一个类。
@Document("store-#{@environment.getProperty('collection.suffix')}")
public class Store {
@Id
private String id;
private String name;
// getters and setters
}
Then, let’s create our first store:
然后,让我们创建我们的第一个商店。
$ curl -X POST 'http://localhost:8080/music/store' -H 'Content-Type: application/json' -d '{
"name": "Store A"
}'
{ "id": "62744c6267d3a034ec5e5719" }
As a result, our class will be stored in a collection named “store-db”. Again, we can verify our configuration by listing its contents:
结果是,我们的类将被存储在一个名为 “store-db “的集合中。同样,我们可以通过列出其内容来验证我们的配置。
$ curl 'http://localhost:8080/collection/store-db'
[
{
"name": "Store A",
"_class": "com.baeldung.boot.collection.name.data.Store"
}
]
That way, if we change our suffix, we don’t have to manually change it in every class. Instead, we just update our properties file. The downside is that it’s more boilerplate, and we still have to write the class name anyway. However, it can also help with multitenancy support. For example, instead of a suffix, we could have the tenant ID to mark collections that are not shared.
这样,如果我们改变我们的后缀,我们就不必在每个类中手动改变它。相反,我们只需更新我们的属性文件。缺点是这是更多的模板,而且我们仍然必须写出类的名字。然而,它还可以帮助实现multitenancy支持。例如,我们可以用租户ID代替后缀,来标记不共享的集合。
3.4. Using a Bean Method With SpEL
3.4.在SpEL中使用Bean方法
Another downside of using SpEL is that it requires extra work to evaluate programmatically. But, it opens up a lot of possibilities, like calling any bean method to determine our collection name. So, in our next example, we’ll create a bean to fix a collection name to conform with our naming rules.
使用SpEL的另一个缺点是,它需要额外的工作来进行程序化评估。但是,它开启了很多可能性,比如调用任何Bean方法来确定我们的集合名称。所以,在我们的下一个例子中,我们将创建一个Bean来固定一个集合的名称,以符合我们的命名规则。
In our naming strategy, we’ll use snake-case. First, let’s borrow code from Spring Data’s SnakeCaseFieldNamingStrategy to create our utility bean:
在我们的命名策略中,我们将使用蛇形码。首先,让我们借用Spring Data的SnakeCaseFieldNamingStrategy的代码来创建我们的实用Bean。
public class Naming {
public String fix(String name) {
List<String> parts = ParsingUtils.splitCamelCaseToLower(name);
List<String> result = new ArrayList<>();
for (String part : parts) {
if (StringUtils.hasText(part)) {
result.add(part);
}
}
return StringUtils.collectionToDelimitedString(result, "_");
}
}
Next, let’s add that bean to our application:
接下来,让我们把这个bean添加到我们的应用程序中。
@SpringBootApplication
public class SpringBootCollectionNameApplication {
// main method
@Bean
public Naming naming() {
return new Naming();
}
}
After that, we’ll be able to reference our bean via SpEL:
之后,我们就可以通过SpEL来引用我们的bean。
@Document("#{@naming.fix('MusicTrack')}")
public class MusicTrack {
@Id
private String id;
private String name;
private String artist;
// getters and setters
}
Let’s try it by adding an item to our collection:
让我们尝试一下,向我们的收藏品添加一个项目。
$ curl -X POST 'http://localhost:8080/music/track' -H 'Content-Type: application/json' -d '{
"name": "Track 1",
"artist":"Artist A"
}'
{ "id": "62755987ae94c5278b9530cc" }
Consequently, our track will be stored in a collection named “music_track”:
因此,我们的轨道将被存储在一个名为 “music_track “的集合中。
$ curl 'http://localhost:8080/collection/music_track'
[
{
"name": "Track 1",
"artist": "Artist A",
"_class": "com.baeldung.boot.collection.name.data.MusicTrack"
}
]
Unfortunately, there’s no way to obtain the class name dynamically, which is a major downside, but it allows for changing our database naming rules without having to manually rename all our classes.
不幸的是,没有办法动态地获得类的名称,这是一个主要的缺点,但它允许改变我们的数据库命名规则,而不必手动重新命名所有的类。
4. Conclusion
4.总结
In this article, we explored the ways we can configure our collection names using the tools Spring Data provides us. Further, we saw the advantages and disadvantages of each, so we can decide what works best for a specific scenario. During that, we built a simple use case to showcase the different approaches.
在本文中,我们探讨了使用 Spring Data 提供的工具来配置我们的集合名称的方法。此外,我们看到了每种方式的优缺点,因此我们可以决定哪种方式最适合特定场景。在此期间,我们构建了一个简单的用例来展示不同的方法。
And as always, the source code is available over on GitHub.
一如既往,源代码可在GitHub上获得。