在 Spring WebFlux 中,如何链接来自多个服务/存储库的方法,以便“删除”多个 DB-Collections 中的元素?

Posted

技术标签:

【中文标题】在 Spring WebFlux 中,如何链接来自多个服务/存储库的方法,以便“删除”多个 DB-Collections 中的元素?【英文标题】:In Spring WebFlux, How to chaining methods from multiple services/repo, in order to 'delete' elements in multiples DB-Collections'? 【发布时间】:2021-08-26 16:15:14 【问题描述】:

1) 关于问题的语境化:

我正在尝试使用 03 个不同的服务/存储库(userService + postService + userRepo)在 03 个不同的 DB-Collections(Reactive MongoDB)中“删除”项目;

我的目标是使用相同的链接代码同时删除一个对象(在每个集合中);

下面是上述情况的代码:

1.1) 代码:

当前工作状态:不工作;

当前行为:不执行任何删除,无论是 delete-userService、delete-postService 还是 delete-userRepo。

@Slf4j
@Service
@AllArgsConstructor
public class UserService implements UserServiceInt 

  private final UserRepo userRepo;

  private final PostServiceInt postServ;

  private final CommentServiceInt comServ;

  private final CustomExceptions customExceptions;

  @Override
  public Mono<Void> deleteInThreeCollections(String id) 
    return userRepo
           .findById(id)
           .switchIfEmpty(customExceptions.userNotFoundException())
           
           .map(user -> 
             userRepo.delete(user); // First deletion - delete-method from userRepo
             return user;
           )

           .flatMapMany(user -> postServ.findPostsByAuthorId(user.getId()))
           .map(post -> 
             postServ.delete(post); // Second deletion - delete-method from postService
             return post;
           )

           .flatMap(post -> comServ.findCommentsByPostId(post.getPostId()))
           .map(comServ::delete) // Third deletion - delete-method from commentService
           .then()
           ;
  


2) 问题:

如何删除不同 DB-Collections 中的不同元素, 仅使用一个“链式删除方法”,该方法使用三个“删除方法” 三种不同的服务/repo(userService + postService + userRepo?

3) 更新:

找到解决方案

@Override
 public Mono<Void> deleteInThreeCollections(String id) 
  return userRepo
         .findById(id)
         .switchIfEmpty(customExceptions.userNotFoundException())
         .flatMap(
                user -> postServ
                       .findPostsByAuthorId(user.getId())
                       .flatMap(
                              post -> comServ.findCommentsByPostId(
                                     post.getPostId())
                                             .flatMap(comServ::delete)
                                             .thenMany(
                                                    postServ.findPostsByAuthorId(
                                                           post.getAuthor()
                                                               .getId()))
                                             .flatMap(postServ::delete)
                               )
                       .then(userRepo.delete(user))
                 );
 

非常感谢您的帮助

【问题讨论】:

【参考方案1】:

假设您的someclass.delete() 操作返回Mono&lt;Something&gt;

主要问题是map不会订阅内部发布者。

这里你必须使用flatMap/concatMap...操作,它们会订阅内部发布者(例如xyz.delete)

如果你的delete方法没有返回被删除的项,你可以使用then(object)返回给定的参数。

正如我所见,您不能同时执行所有删除操作,而是按顺序执行,因为每个操作的输入参数都是前一个操作的输出。如果您考虑批量处理所有帖子或 cmets.. 同时删除,这是可能的。您可以collectList id-s 并创建批处理操作 (deleteByIdIn(list of ids)) 或创建并行通量并同时运行删除操作(批处理更好)。

在这里,我创建了一个带有假服务的最小示例来演示它:

import org.junit.jupiter.api.Test
import reactor.core.publisher.Flux
import reactor.core.publisher.Mono
import reactor.test.StepVerifier

data class User(
    val id: String
)

data class Post(
    val id: String
)

data class Comment(
    val id: String
)

class UserRepo 

    fun finById(id: String): Mono<User> 
        println("Find an user")
        return Mono.just(User("1"))
    

    //example if delete gives back the object
    fun delete(user: User): Mono<User> 
        println("delete $user")
        return Mono.just(user)
    



class PostServiceInt 

    fun findPostsByAuthorId(userId: String): Flux<Post> = Flux.fromIterable(listOf(Post("1"), Post("2")))

    //and if not
    fun delete(post: Post): Mono<Void> 
        println("delete $post")
        return Mono.empty()
    




class CommentServiceInt 

    fun findCommentsByPostId(postId: String): Flux<Comment> = Flux.fromIterable(listOf(Comment("10"), Comment("7"),Comment("3"),Comment("4")))

    fun delete(comment: Comment): Mono<Comment> 
        println("delete $comment")
        return Mono.just(comment)
    




class Example 

    val userRepo = UserRepo()
    val postServ = PostServiceInt()
    val comServ = CommentServiceInt()

    @Test
    fun test() 
        val result = userRepo.finById("1")
            .switchIfEmpty(Mono.error  RuntimeException() )
            .flatMap  user -> userRepo.delete(user) 
            .flatMapMany  user -> postServ.findPostsByAuthorId(user.id) 
            .flatMap  post -> postServ.delete(post).then(Mono.just(post)) 
            .flatMap  post -> comServ.findCommentsByPostId(post.id) 
            .flatMap  comment -> comServ.delete(comment) 

        StepVerifier.create(result)
            .expectNextCount(4)
            .verifyComplete()


    
    

【讨论】:

伙计,非常感谢。你的帮助非常有帮助。感谢您花时间帮助我。非常感谢

以上是关于在 Spring WebFlux 中,如何链接来自多个服务/存储库的方法,以便“删除”多个 DB-Collections 中的元素?的主要内容,如果未能解决你的问题,请参考以下文章

我们可以在 Spring Webflux 中使用 web servlet 和 servlet 过滤器吗?

服务器发送事件 Spring Webflux 返回事件名称以响应来自休息控制器的响应

Spring WebFlux:从控制器提供文件

在 spring boot 基于 webflux 的微服务中,订阅者是谁?

如何在 Spring boot 2 + Webflux + Thymeleaf 中配置 i18n?

使用Webflux和Spring Cloud时如何用netty替换tomcat?