如何将数据库引用与反应性 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。
我看到了两个解决方案:
拿到pojo就填:
public Mono<User> getUser(String login) {
return userRepository.findByLogin(login)
.flatMap(user -> ??? );
}
我应该用 profileRepository.findAllById() 做点什么,但我不知道或连接两个发布者,因为配置文件结果取决于用户结果。
- 声明一个 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 的命令式(阻塞)实现使用命令式方法获取 Document
s 并将它们转换为域对象。 MappingMongoConverter
特别处理所有转换和 DBRef
解析。此 API 在 synchronous/imperative API 中工作,并用于两个模板 API 实现(命令式和反应式)。
重用 MappingMongoConverter
是添加响应式支持的合乎逻辑的决定,因为我们不需要重复代码。唯一的限制是 DBRef
分辨率不适合反应式执行模型。
为了支持反应式 DBRef
s,转换器需要拆分成几个位,整个关联处理需要大修。
参考:https://jira.spring.io/browse/DATAMONGO-2146
推荐
在您的域模型中将引用保留为 keys/Id,并根据需要查找这些引用。 zipWith
和 flatMap
是合适的运算符,具体取决于您要存档的内容(使用引用增强模型,仅查找引用)。
相关说明:反应性 Spring 数据 MongoDB 部分带有缩减的功能集。上下文 SpEL 扩展是一项不受支持的功能,因为这些组件采用命令式编程模型并因此采用同步执行。
就我而言,我使用以下方法解决了这个问题:
- 我的实体是:
@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<>();
}
- 我的控制器是:
@GetMapping(FIND_POST_BY_ID_SHOW_COMMENTS)
@ResponseStatus(OK)
public Mono<Post> findPostByIdShowComments(@PathVariable String id) {
return postService.findPostByIdShowComments(id);
}
- 最后但并非最不重要的,我的服务(这里是解决方案):
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
我是 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。
我看到了两个解决方案:
拿到pojo就填:
public Mono<User> getUser(String login) { return userRepository.findByLogin(login) .flatMap(user -> ??? ); }
我应该用 profileRepository.findAllById() 做点什么,但我不知道或连接两个发布者,因为配置文件结果取决于用户结果。
- 声明一个 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 的命令式(阻塞)实现使用命令式方法获取 Document
s 并将它们转换为域对象。 MappingMongoConverter
特别处理所有转换和 DBRef
解析。此 API 在 synchronous/imperative API 中工作,并用于两个模板 API 实现(命令式和反应式)。
重用 MappingMongoConverter
是添加响应式支持的合乎逻辑的决定,因为我们不需要重复代码。唯一的限制是 DBRef
分辨率不适合反应式执行模型。
为了支持反应式 DBRef
s,转换器需要拆分成几个位,整个关联处理需要大修。
参考:https://jira.spring.io/browse/DATAMONGO-2146
推荐
在您的域模型中将引用保留为 keys/Id,并根据需要查找这些引用。 zipWith
和 flatMap
是合适的运算符,具体取决于您要存档的内容(使用引用增强模型,仅查找引用)。
相关说明:反应性 Spring 数据 MongoDB 部分带有缩减的功能集。上下文 SpEL 扩展是一项不受支持的功能,因为这些组件采用命令式编程模型并因此采用同步执行。
就我而言,我使用以下方法解决了这个问题:
- 我的实体是:
@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<>();
}
- 我的控制器是:
@GetMapping(FIND_POST_BY_ID_SHOW_COMMENTS)
@ResponseStatus(OK)
public Mono<Post> findPostByIdShowComments(@PathVariable String id) {
return postService.findPostByIdShowComments(id);
}
- 最后但并非最不重要的,我的服务(这里是解决方案):
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