如何避免依赖前置结果的多嵌套Repository调用回调地狱?
问题描述
业务场景:先向数据库表中保存一条EntityDir记录,保存完成后需判断:若该记录的Parent为空,则检查Connector表中是否存在CreatorID对应的EntityConnectorUserToDir记录——若不存在则新增该记录;若存在则更新其目录列表后重新保存。
当前实现问题:采用嵌套flatMap的方式实现,代码结构混乱;尝试过Mono.block()但无效,且不符合非阻塞编程风格,寻求更优实现方案。
当前实现代码(中文注释版):
Mono<EntityDir> savedDirMono = dirRepo.save(dir); // 保存到数据库 // 需要检查新保存的entityDir是否需要同步到Connector表,逻辑在这个flatMap链里 return savedDirMono.flatMap( savedDir -> { // 满足条件时,需要将savedDir添加到Connector if ((savedDir.getParent() == null || savedDir.getParent().isEmpty())) { // 检查是否存在相同userID的记录,存在则取出更新后重新保存到Connector表 return connectorUserToDirRepo .findById(savedDir.getCreatorID()) .defaultIfEmpty(new EntityConnectorUserToDir(null, null)) .flatMap(savedConnector -> { // 无记录时直接新增 if (savedConnector.getUserID() == null || savedConnector.getUserID().isEmpty()) { List<String> stringList = new ArrayList<>(); stringList.add(savedDir.get_id()); // 保存到另一张表后,映射回savedDir return connectorUserToDirRepo.save(new EntityConnectorUserToDir(savedDir.getCreatorID(), stringList)) .map(connectorUserToDir -> savedDir); } else { // 记录存在,更新目录列表后重新保存 List<String> stringList = savedConnector.getDirs(); stringList.add(savedDir.get_id()); // 保存到Connector表后,映射回savedDir return connectorUserToDirRepo.save(savedConnector) .map(connectorUserToDir -> savedDir); } }); } else { // 不是根目录,无需后续操作 log.info("NOT A PARENT"); return Mono.justOrEmpty(savedDir); } } );
优化方案
核心思路是拆分逻辑、简化嵌套、遵循响应式编程的链式调用风格,同时保证非阻塞特性:
public Mono<EntityDir> saveDirAndSyncConnector(EntityDir dir) { return dirRepo.save(dir) .flatMap(this::syncToConnectorIfRootDir); } private Mono<EntityDir> syncToConnectorIfRootDir(EntityDir savedDir) { // 非根目录直接返回 if (savedDir.getParent() != null && !savedDir.getParent().isEmpty()) { log.info("NOT A PARENT"); return Mono.just(savedDir); } String creatorId = savedDir.getCreatorID(); String dirId = savedDir.get_id(); // 查询Connector记录,不存在则新增,存在则更新目录列表 return connectorUserToDirRepo.findById(creatorId) // 无记录时直接构建新对象保存 .switchIfEmpty(Mono.defer(() -> { List<String> dirList = new ArrayList<>(); dirList.add(dirId); return connectorUserToDirRepo.save(new EntityConnectorUserToDir(creatorId, dirList)); })) // 有记录时更新目录列表后保存 .flatMap(existingConnector -> { List<String> dirList = existingConnector.getDirs(); // 防御性判空,避免空指针 if (dirList == null) { dirList = new ArrayList<>(); } dirList.add(dirId); existingConnector.setDirs(dirList); return connectorUserToDirRepo.save(existingConnector); }) // 无论Connector操作结果如何,最终返回保存的EntityDir .then(Mono.just(savedDir)); }
优化点说明
- 逻辑拆分:将同步Connector表的逻辑抽成独立方法,主流程仅负责保存目录+触发同步,结构更清晰;
- 避免空对象判断:用
switchIfEmpty替代defaultIfEmpty+空对象检查,直接在无记录时执行新增逻辑,减少嵌套和空值处理; - 防御性编程:对
existingConnector.getDirs()做判空,避免潜在的空指针异常; - 链式调用更简洁:去掉多层嵌套
flatMap,改用线性链式调用,符合Reactor编程习惯,可读性大幅提升; - 非阻塞保证:全程使用Mono操作符,未调用
block(),完全遵循响应式非阻塞风格。
内容的提问来源于stack exchange,提问作者Kuchuk borom Debbarma
相关产品推荐
相关产品推荐

