12.3 编写反应式的MongoDB repository

12.3.1 启用Spring Data MongoDB

  1. 添加依赖

如果需要非反应式则添加:

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-data-mongodb</artifactId>
  4. </dependency>

反应式则添加:

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>
  4. </dependency>
  1. 添加嵌入式的MongoDB

    1. <dependency>
    2. <groupId>de.flapdoodle.embed</groupId>
    3. <artifactId>de.flapdoodle.embed.mongo</artifactId>
    4. </dependency>
  2. 如果是生产环境,则不使用嵌入式数据库,还需要配置几个属性:

    1. spring:
    2. data:
    3. mongodb:
    4. host: xxxx
    5. port: 27018
    6. username: xxxx
    7. password: xxxx
    8. database: xxxx

    12.3.2 将领域对象映射为文档

  • @Id: 将某个属性指明为文档的ID
  • @Document: 将领域类型声明为要持久化到MongoDB中的文档
  • @Field: 指定某个属性持久化到文档中的字段名称

    1. @Data
    2. @RequiredArgsConstructor
    3. @NoArgsConstructor(access = AccessLevel.PRIVATE, force = true)
    4. @Document(collection = "ingredients")
    5. public class Ingredient {
    6. @Id
    7. private final String id;
    8. private final String name;
    9. private final Type type;
    10. public static enum Type {
    11. WRAP, PROTEIN, VEGGIES, CHEESE, SAUCE
    12. }
    13. }

    @Id可以用到任意Serializable类型的字段上,包括String和Long @Document(collection = “ingredients”): 如果没有特别指定,则会持久化到类名首字母小写(ingredient)的集合中,可以通过collection属性来改变这种行为

  1. @Data
  2. @Document
  3. public class Taco {
  4. @Id
  5. private String id;
  6. @NotNull
  7. @Size(min = 5, message = "name must be at least 5 characters long")
  8. private String name;
  9. private Date createdAt = new Date();
  10. @Size(min = 1, message = "You must choose at least 1 ingredient")
  11. private List<Ingredient> ingredients;
  12. }
  1. @Data
  2. @Document
  3. public class Order implements Serializable {
  4. private static final long serialVersionUID = 1L;
  5. @Id
  6. private String id;
  7. private Date placedAt = new Date();
  8. @Field("customer")
  9. private User user;
  10. private String deliveryName;
  11. private String deliveryStreet;
  12. private String deliveryCity;
  13. private String deliveryState;
  14. private String deliveryZip;
  15. private String ccNumber;
  16. private String ccExpiration;
  17. private String ccCVV;
  18. private List<Taco> tacos = new ArrayList<>();
  19. public void addDesign(Taco design) {
  20. this.tacos.add(design);
  21. }
  22. }

12.3.3 编写反应式的MongoDB repository接口

有两种接口选择:ReactiveCrudRepository和ReactiveMongoRepository
他们之间的区别在于:ReactiveMongoRepository提供多个特殊的insert()⽅法,它们针对新⽂档的持久化进⾏了优化,⽽ReactiveCrudRepository依赖save()⽅法来保存新⽂档和已有的⽂档
在数据库初始化完成之后,我们不会频繁地创建配料的⽂档,甚⾄有可能永远不会这样做。因此, ReactiveMongoRepository提供的优化没有太多的⽤处,我们可以让 IngredientRepository扩展ReactiveCrudRepository:

  1. public interface IngredientRepository extends ReactiveCrudRepository<Ingredient, String> {}

为了将Taco持久化为MongoDB中的⽂档,我们定义另⼀个repository。 与配料⽂档不同,我们会频繁创建taco⽂档。因此,ReactiveMongoRepository优化过的insert()⽅法就很有价值了。

  1. public interface TacoRepository extends ReactiveMongoRepository<Taco, String> {
  2. Flux<Taco> findByOrderByCreatedAt();
  3. }
  1. public interface OrderRepository extends ReactiveMongoRepository<Order, String> {
  2. }
  1. public interface UserRepository extends ReactiveMongoRepository<User, String> {
  2. Mono<User> findByUsername(String username);
  3. }