为Spring Data中的类配置MongoDB集合名称

2023/05/18

1. 概述

在本教程中,我们将学习如何为我们的类配置MongoDB集合名称,以及一个实际示例。我们将使用Spring Data,它为我们提供了一些选项,只需很少的配置即可实现此目的。我们将通过构建一个简单的音乐商店来探索每个选项。这样,我们就可以找出何时使用它们是有意义的。

2. 用例和设置

我们的用例有四个简单的类:MusicAlbum、Compilation、MusicTrack和Store,每个类都将以不同的方式配置其集合名称。此外,每个类都有自己的MongoRepository,不需要自定义查询方法。此外,我们需要一个正确配置的MongoDB数据库实例。

2.1 按名称列出集合内容的服务

首先,让我们编写一个控制器来断言我们的配置正在生效。我们将通过按集合名称搜索来做到这一点。请注意,在使用Repository时,集合名称配置是透明的:

@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);
    }
}

该控制器基于MongoTemplate并使用通用类型DBObject,它不依赖于类和Repository。此外,通过这种方式,我们将能够看到内部MongoDB属性。最重要的是,Spring Data用来解组JSON对象的“_class”属性保证了我们的配置是正确的。

2.2 API服务

其次,我们将开始构建我们的Service,它只是保存对象并检索它们的集合。稍后将在我们的第一个配置示例中创建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端点

最后,让我们编写一个控制器来与我们的应用程序交互。我们将为Service方法公开端点:

@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
}

之后,我们就可以开始配置我们的类了。

3. 配置@Document注解

从Spring Data版本1.9开始可用,@Document注解可以完成我们需要的一切。它特定于MongoDB,但类似于JPA的@Entity注解。在撰写本文时,无法为集合名称定义命名策略,只能为字段名称定义命名策略。因此,让我们探索一下可用的内容。

3.1 默认行为

默认行为认为集合名称与类名称相同,但以小写字母开头。简而言之,我们只需要添加Document注解,它就会起作用

@Document
public class Compilation {
    @Id
    private String id;

    // getters and setters
}

之后,我们CompilationRepository中的所有插入都将进入MongoDB中名为“compilation”的集合:

$ curl -X POST http://localhost:8080/music/compilation -H 'Content-Type: application/json' -d '{
    "name": "Spring Hits"
}'

{ "id": "6272e26e04a673360d926ca1" }

让我们列出“compilation”集合的内容来验证我们的配置:

$ curl http://localhost:8080/collection/compilation

[
    {
        "name": "Spring Hits",
        "_class": "cn.tuyucheng.taketoday.boot.collection.name.data.Compilation"
    }
]

这是配置集合名称最干净的方法,因为它与我们的类名基本相同。一个缺点是,如果我们决定改变我们的数据库命名约定,则需要重构我们所有的类。例如,如果我们决定使用蛇形大小写作为集合名称,我们将无法利用默认行为。

3.2 重写value属性

@Document注解允许我们可以使用collection属性覆盖默认行为。由于此属性是value属性的别名,我们可以隐式设置它:

@Document("albums")
public class MusicAlbum {
    @Id
    private String id;

    private String name;

    private String artist;

    // getters and setters
}

现在,文档不再进入名为“musicAlbum”的集合,而是进入“albums”集合。这是使用Spring Data配置集合名称的最简单方法。要查看它的实际效果,让我们添加一个MusicAlbum到我们的集合中:

$ curl -X POST 'http://localhost:8080/music/album' -H 'Content-Type: application/json' -d '{ 
    "name": "Album 1",
    "artist": "Artist A"
}'

{ "id": "62740de003d2452a61a75c35" }

然后,我们可以获取我们的“albums”集合,确保我们的配置有效:

$ curl 'http://localhost:8080/collection/albums'

[
    {
        "name": "Album 1",
        "artist": "Artist A",
        "_class": "cn.tuyucheng.taketoday.boot.collection.name.data.MusicAlbum"
    }
]

此外,它非常适合让我们的应用程序适应现有数据库,其中的集合名称与我们的类不匹配。一个缺点是如果我们需要添加默认前缀,则需要为每个类添加它。

3.3 将配置属性与SpEL一起使用

这种组合可以帮助完成单独使用@Document注解无法完成的事情,我们将从我们希望在我们的类中重用的特定于应用程序的属性开始。

首先,让我们向application.properties添加一个属性,我们将该属性用作集合名称的后缀

collection.suffix=db

现在,让我们通过Environment bean使用SpEL引用它来创建我们的下一个类:

@Document("store-#{@environment.getProperty('collection.suffix')}")
public class Store {
    @Id
    private String id;

    private String name;

    // getters and setters
}

然后,保存我们的第一个Store对象:

$ curl -X POST 'http://localhost:8080/music/store' -H 'Content-Type: application/json' -d '{
    "name": "Store A"
}'

{ "id": "62744c6267d3a034ec5e5719" }

因此,我们的类将存储在名为“store-db”的集合中。同样,我们可以通过列出其内容来验证我们的配置:

$ curl 'http://localhost:8080/collection/store-db'

[
    {
        "name": "Store A",
        "_class": "cn.tuyucheng.taketoday.boot.collection.name.data.Store"
    }
]

这样,如果我们更改后缀,就不必在每个类中手动更改它。相反,我们只是更新我们的属性文件。缺点是样板代码多,无论如何我们仍然必须编写类名。但是,它也可以帮助提供多租户支持。例如,我们可以使用租户ID来标记未共享的集合,而不是后缀

3.4 将Bean方法与SpEL结合使用

使用SpEL的另一个缺点是它需要额外的工作来以编程方式进行评估。但是,它开辟了很多可能性,比如调用任何bean方法来确定我们的集合名称。因此,在我们的下一个示例中,我们将创建一个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, "_");
    }
}

接下来,让我们将该bean添加到我们的应用程序中:

@SpringBootApplication
public class SpringBootCollectionNameApplication {

    // main method

    @Bean
    public Naming naming() {
        return new Naming();
    }
}

之后,我们就能够通过SpEL引用我们的bean:

@Document("#{@naming.fix('MusicTrack')}")
public class MusicTrack {
    @Id
    private String id;

    private String name;

    private String artist;

    // getters and setters
}

让我们通过向我们的集合中添加一个元素来尝试一下:

$ curl -X POST 'http://localhost:8080/music/track' -H 'Content-Type: application/json' -d '{
    "name": "Track 1",
    "artist":"Artist A"
}'

{ "id": "62755987ae94c5278b9530cc" }

因此,我们的MusicTrack将存储在名为“music_track”的集合中:

$ curl 'http://localhost:8080/collection/music_track'

[
    {
        "name": "Track 1",
        "artist": "Artist A",
        "_class": "cn.tuyucheng.taketoday.boot.collection.name.data.MusicTrack"
    }
]

不幸的是,没有办法动态获取类名,这是一个主要缺点,但它允许更改我们的数据库命名规则,而无需手动重命名我们所有的类。

4. 总结

在本文中,我们探讨了使用Spring Data为我们提供的工具配置集合名称的方法。此外,我们看到了每种方法的优点和缺点,因此我们可以决定哪种方法最适合特定场景。在此期间,我们构建了一个简单的用例来演示不同的方法。

与往常一样,本教程的完整源代码可在GitHub上获得。

Show Disqus Comments

Post Directory

扫码关注公众号:Taketoday
发送 290992
即可立即永久解锁本站全部文章