如何使用Reactor与R2dbc实现祖孙层级关联数据的完整查询填充
解决方案:嵌套关联的响应式查询填充
首先先修正实体类里的两处语法错误(少逗号),避免编译失败:
@Entity @Table(name = "child", schema = "public", catalog = "Test") data class MyChildObject( @GeneratedValue(strategy = GenerationType.IDENTITY) @Id @org.springframework.data.annotation.Id @Column(name = "id") var id: Int = 0, @Column(name = "parent_id") var parentId: Int? = null, // 补全缺失逗号 @Transient var grandchildData: List<MyGrandchildObject>? = null ) @Entity @Table(name = "grandchild", schema = "public", catalog = "Test") data class MyGrandchildObject( @GeneratedValue(strategy = GenerationType.IDENTITY) @Id @org.springframework.data.annotation.Id @Column(name = "id") var id: Int = 0, // 补全缺失逗号 @Column(name = "child_id") var childId: Int? = null )
接下来你只需要调整关联数据填充的逻辑,把Child和Grandchild的填充流程嵌套起来就行——完全可以复用你已经写好的addGrandchildData方法,不需要修改实体类属性类型。修改后的代码如下:
fun viewAllParents(): Mono<MutableList<MyParentObject>> = parentRepository.findAll() .flatMap { parent -> // 为当前Parent填充带Grandchild数据的Child列表 Mono.just(parent).addChildWithGrandchildData(parent.id) } .collectList() // 新增方法:为Parent填充已关联Grandchild的Child数据 fun Mono<MyParentObject>.addChildWithGrandchildData(parentId: Int): Mono<MyParentObject> = this.zipWith( // 查询当前Parent下的所有Child,同时为每个Child填充Grandchild childRepository.getAllByParentIdEquals(parentId) .flatMap { child -> Mono.just(child).addGrandchildData(child.id) } .collectList() ) .map { tuple -> tuple.t1.childData = tuple.t2 tuple.t1 } // 复用你已实现的方法,注意修正变量名一致性 fun Mono<MyChildObject>.addGrandchildData(id: Int): Mono<MyChildObject> = this.zipWith(childOfChildRepository.getAllByChildIdEquals(id).collectList()) .map { it.t1.grandchildData = it.t2 // 这里要和实体类属性名保持一致 it.t1 }
关键思路说明:
- 响应式嵌套串联:R2DBC是异步非阻塞模型,我们用
flatMap来层级处理查询——先查Parent,再查该Parent下的所有Child,接着对每个Child触发Grandchild的查询,最后把所有结果组装到实体的临时属性中。 - 复用已有逻辑:你已经实现了
addGrandchildData,直接在Child的查询流中调用即可,不需要重复造轮子。 - 无需修改属性类型:完全不需要把
grandchildData改成Flux,我们通过collectList()把Flux<MyGrandchildObject>转换成Mono<List<MyGrandchildObject>>,正好可以赋值给实体类里的List<MyGrandchildObject>?属性。
额外优化建议:
- 如果数据量较大,可以考虑在Repository层编写JOIN语句的关联查询,减少数据库交互次数,提升性能;如果数据量不大,当前写法的可读性和维护性已经足够好。
- 注意变量名一致性:你之前的代码里写了
childOfChildData,但实体类属性是grandchildData,这个小细节要修正,避免赋值失败。
内容的提问来源于stack exchange,提问作者AlexJ
相关产品推荐
相关产品推荐

