Spring Boot集成MongoDB时findAll()返回空列表但集合存在文档
问题场景
- 基于Spring Boot开发的MongoDB对接应用,启动阶段数据库连接流程正常,无连接类异常
- 调用
MongoRepository提供的findAll()方法时,仓储层返回空列表,但目标集合中实际存在1条有效文档 - 访问
http://localhost:8080/api/v1/user/getAllUsers接口时,无异常堆栈抛出,仅输出流程埋点日志
涉及的代码定义:
仓储层
@Repository public interface UserDao extends MongoRepository<User, String>{ }
服务层
@Component @Slf4j public class UserServiceImpl implements IUser { @Autowired UserDao userDao; @Override public List<User> getAllUsers() throws Exception { log.info("[ getAllUsers ] Start"); List<User> response = new ArrayList<User>(); try { log.debug("Calling user Dao to select all users"); response = userDao.findAll(); } catch (Exception e) { log.error(Constants.COMMENT_SELECTION_ERROR, e); throw new Exception(Constants.COMMENT_SELECTION_ERROR, e); } log.debug("Response: count of users :{} ", response.size()); log.info("[ getAllUsers ] End :: Success"); return response; } }
控制器层
@RestController @Slf4j @SuppressWarnings("rawtypes") @RequestMapping(value = "/api/v1/user") public class UserApiController { @Autowired private IUser sUser; @ApiOperation(value = "getAllUsers", response = ResponseEntity.class) @ApiResponses(value = { @ApiResponse(code = 200, message = "selection successful", response = User.class), @ApiResponse(code = 400, message = "Required parameter does not exist or is not in the expected format", response = User.class), @ApiResponse(code = 401, message = "You are not authorized to view the resource", response = User.class), @ApiResponse(code = 406, message = "The resource you were trying to reach is not found", response = User.class), @ApiResponse(code = 500, message = "Accessing the resource you were trying to reach is forbidden") }) @RequestMapping(value = "/getAllUsers", method = RequestMethod.GET, produces = "application/json") ResponseEntity<List<User>> getAllUsers() { log.info("[ getAllUsers ] Start"); List<User> responseListUsers = new ArrayList<>(); try { responseListUsers = sUser.getAllUsers(); log.info("[ getAllUsers ] End :: Success"); return new ResponseEntity<>(responseListUsers, HttpStatus.OK); } catch (Exception se) { log.error("[ getAllUsers ] End :: ERROR", se.getMessage()); return new ResponseEntity<>(responseListUsers, HttpStatus.INTERNAL_SERVER_ERROR); } } }
实体模型
@ApiModel(value = "User", description = "User Model") @Data @Document @NoArgsConstructor public class User { String id; @ApiModelProperty(value = "name", required = true) @NotBlank private String name; @ApiModelProperty(value = "address", required = true) @NotBlank private String address; @ApiModelProperty(value = "phone", required = true) @NotNull @Pattern(regexp = Constants.PHONE_FORMAT) private String phone; @ApiModelProperty(value = "job", required = true) @NotBlank private String job; @ApiModelProperty(value = "agency", required = true) @DBRef private Agency agency; }
目标集合中的文档结构参考:
接口返回时无异常输出,日志仅包含流程埋点:
2021-03-31 15:51:51.023 INFO 19804 --- [nio-8080-exec-1] c.c.airbnb.controller.UserApiController : [ getAllUsers ] Start
2021-03-31 15:51:51.025 INFO 19804 --- [nio-8080-exec-1] c.c.airbnb.service.impl.UserServiceImpl : [ getAllUsers ] Start
2021-03-31 15:51:51.035 INFO 19804 --- [nio-8080-exec-1] c.c.airbnb.service.impl.UserServiceImpl : [ getAllUsers ] End :: Success
2021-03-31 15:51:51.037 INFO 19804 --- [nio-8080-exec-1] c.c.airbnb.controller.UserApiController : [ getAllUsers ] End :: Success
核心排查方向与解决方案
1. 集合名大小写不匹配(最高发问题)
- Spring Data MongoDB 对未手动指定集合名的
@Document实体,默认的集合名映射规则是类名首字母小写,即User实体默认查询的集合是user - MongoDB 原生对集合名大小写敏感,如果实际存储文档的集合名是大写开头的
User,查询时会因为集合名不匹配直接返回空结果,全程无异常抛出 - 解决方案:在实体类的
@Document注解上手动指定和实际存储完全一致的集合名,比如:@Document(collection = "实际存储的集合准确名称") public class User { // 字段定义 }
2. 连接的数据库名和实际存数库不匹配
- 校验MongoDB连接串中的
/后跟随的数据库名,是否和你查看文档所在的数据库完全一致 - 这类问题常见于使用可视化工具查看数据时,误将其他同名库下的集合当成了目标库,应用实际连接的是另一个空数据库,连接过程正常但查询结果为空
3. @DBRef字段的静默解析过滤
- 实体类中关联的
Agency字段标注了@DBRef,要求MongoDB中存储的文档对应的agency字段必须是标准DBRef结构(包含$ref、$id属性) - 部分Spring Data MongoDB版本遇到字段结构不符合DBRef要求时,不会抛出解析异常,而是直接将该文档从返回结果中过滤,最终导致查询返回空列表
- 校验方法:临时去掉
agency字段的@DBRef注解,或简化实体类只保留id、name等基础字段,重新调用接口测试,如果能查到数据,就是DBRef字段的结构不匹配导致的问题,调整文档中的agency字段结构即可
4. 开启查询日志校验实际执行语句
- 在配置文件中开启MongoDB查询的DEBUG日志,定位实际执行的查询目标:
logging: level: org.springframework.data.mongodb.core.MongoTemplate: DEBUG - 调用接口后,将日志打印的查询语句(目标库、目标集合、过滤条件)直接放到MongoDB Shell或可视化工具中执行,对比返回结果和应用返回结果的差异,就能快速定位不匹配的环节
内容的提问来源于stack exchange,提问作者bretondev
相关产品推荐
相关产品推荐

