如何将数据库引用与反应性 Spring 数据 MongoDB 一起使用?

How to use db references with reactive Spring Data MongoDB?

我是 MongoDB 和 Reactor 的新手,我正在尝试检索与其配置文件关联的用户 这是 POJO :

public class User {

    private @Id String id;
    private String login;
    private String hashPassword;
    @Field("profiles") private List<String> profileObjectIds;
    @Transient private List<Profile> profiles; }

public class Profile {

    private @Id String id;
    private @Indexed(unique = true) String name;
    private List<String> roles; }

问题是,如何在用户 POJO 中注入配置文件?

我知道我可以添加一个@DBRef 来解决问题,但是在它的文档中,MongoDB 指定手动 Ref 应该优先于 DB ref。

我看到了两个解决方案:

  1. 拿到pojo就填:

    public Mono<User> getUser(String login) {
        return userRepository.findByLogin(login)
        .flatMap(user -> ??? );
    }
    

我应该用 profileRepository.findAllById() 做点什么,但我不知道或连接两个发布者,因为配置文件结果取决于用户结果。

  1. 声明一个 AbstractMongoEventListener 并覆盖 onAfterConvert 方法:

但是这里我错了,因为方法在结果发布之前就结束了

public void onAfterConvert(AfterConvertEvent<User> event) {
    final User source = event.getSource();
    source.setProfiles(new ArrayList<>());
    profileRepository.findAllById(source.getProfileObjectIds())
    .doOnNext(e -> source.getProfiles().add(e))
    subscribe();
}

对于第一点,我终于做到了如愿以偿:

public Mono<User> getUser(String login) {
   return userRepository.findByLogin(login)
         .flatMap( user ->
              Mono.just(user)
              .zipWith(profileRepository.findAllById(user.getProfileObjectIds())
                  .collectionList(),
                  (u, p) -> {
                       u.setProfiles(p);
                       return u;
                   })
            );
}

TL;DR

反应式 Spring 数据 MongoDB 中没有 DBRef 支持,我不确定会有。

说明

Spring 数据项目被组织成模板 API、转换器和映射元数据组件。模板 API 的命令式(阻塞)实现使用命令式方法获取 Documents 并将它们转换为域对象。 MappingMongoConverter 特别处理所有转换和 DBRef 解析。此 API 在 synchronous/imperative API 中工作,并用于两个模板 API 实现(命令式和反应式)。

重用 MappingMongoConverter 是添加响应式支持的合乎逻辑的决定,因为我们不需要重复代码。唯一的限制是 DBRef 分辨率不适合反应式执行模型。

为了支持反应式 DBRefs,转换器需要拆分成几个位,整个关联处理需要大修。

参考:https://jira.spring.io/browse/DATAMONGO-2146

推荐

在您的域模型中将引用保留为 keys/Id,并根据需要查找这些引用。 zipWithflatMap 是合适的运算符,具体取决于您要存档的内容(使用引用增强模型,仅查找引用)。

相关说明:反应性 Spring 数据 MongoDB 部分带有缩减的功能集。上下文 SpEL 扩展是一项不受支持的功能,因为这些组件采用命令式编程模型并因此采用同步执行。

就我而言,我使用以下方法解决了这个问题:

  1. 我的实体是:
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Document(collection = "post")
public class Post implements Serializable {

    private static final long serialVersionUID = -6281811500337260230L;

    @EqualsAndHashCode.Include
    @Id
    private String id;
    private Date date;
    private String title;
    private String body;
    private AuthorDto author;
    private Comment comment;
    private List<Comment> listComments = new ArrayList<>();
    private List<String> idComments = new ArrayList<>();
}
  1. 我的控制器是:
    @GetMapping(FIND_POST_BY_ID_SHOW_COMMENTS)
    @ResponseStatus(OK)
    public Mono<Post> findPostByIdShowComments(@PathVariable String id) {
        return postService.findPostByIdShowComments(id);
    }
  1. 最后但并非最不重要的,我的服务(这里是解决方案):
    public Mono<Post> findPostByIdShowComments(String id) {
        return postRepo
                .findById(id)
                .switchIfEmpty(postNotFoundException())
                .flatMap(postFound -> commentService
                                 .findCommentsByPostId(postFound.getId())
                                 .collectList()
                                 .flatMap(comments -> {
                                     postFound.setListComments(comments);
                                     return Mono.just(postFound);
                                 })
                        );
    }

    public Flux<Comment> findCommentsByPostId(String id) {
        return postRepo
                .findById(id)
                .switchIfEmpty(postNotFoundException())
                .thenMany(commentRepo.findAll())
                .filter(comment1 -> comment1.getIdPost()
                                            .equals(id));

    }

谢谢,这帮了大忙。 这是我的解决方案:

public MappingMongoConverter mappingMongoConverter(MongoMappingContext mongoMappingContext) {
    MappingMongoConverter converter = new MappingMongoConverter(NoOpDbRefResolver.INSTANCE, mongoMappingContext);
    converter.setTypeMapper(new DefaultMongoTypeMapper(null));
    converter.setCustomConversions(mongoCustomConversions());
    return converter;
}

诀窍是使用 NoOpDbRefResolver.INSTANCE