如何在Spring Reactive应用中检查数据是否已存在并实现新增逻辑
问题根因
你原有代码的核心问题是逻辑链路顺序错误:findByTitle返回的已存在图书对象、switchIfEmpty返回的新保存成功的图书对象,都会流入最后一段抛异常的flatMap,所以无论新增是否成功都会触发异常。
优化后实现代码
public Mono<Book> saveBook(Mono<Book> book) { return book .doOnNext(b -> log.info("Save book with title: {}", b.getTitle())) .flatMap(requestBook -> // 先按书名查询是否已存在 bookRepository.findByTitle(requestBook.getTitle()) // 查询到结果说明图书已存在,抛出异常 .flatMap(existedBook -> Mono.error( new ResourceAlreadyExistsException( String.format("Book already exists for provided title :: %s", requestBook.getTitle()) ) )) // 未查询到结果,执行保存逻辑 .switchIfEmpty(bookRepository.save(requestBook)) ); }
逻辑说明
- 整条响应式链路仅会在查询到已存在同名图书时触发异常分支
- 无同名图书时直接进入
switchIfEmpty执行保存,返回保存后的Book对象,不会触发异常逻辑 - 去掉了方法上同步抛出
ResourceAlreadyExistsException的声明,响应式场景下异常是封装在Mono的错误信号中传播的,无需同步声明抛出 - 简化了保存逻辑,无需调用
saveAll再取next,直接调用save方法传入实体即可
并发场景优化建议
如果有高并发写入的场景,建议给数据库表的title字段添加唯一索引,避免两个请求同时通过存在性校验后重复插入。可以额外添加异常适配逻辑:
public Mono<Book> saveBook(Mono<Book> book) { return book .doOnNext(b -> log.info("Save book with title: {}", b.getTitle())) .flatMap(requestBook -> bookRepository.findByTitle(requestBook.getTitle()) .flatMap(existedBook -> Mono.error( new ResourceAlreadyExistsException( String.format("Book already exists for provided title :: %s", requestBook.getTitle()) ) )) .switchIfEmpty(bookRepository.save(requestBook)) ) // 捕获数据库唯一索引冲突异常,转换为自定义异常 .onErrorResume(DuplicateKeyException.class, e -> Mono.error(new ResourceAlreadyExistsException("Book already exists")) ); }
内容的提问来源于stack exchange,提问作者pjk
相关产品推荐
相关产品推荐

