Spring Data MongoRepository @Query查询嵌套列表指定Answer对象问题
问题:根据questionId和answerId从MongoDB查询单个Answer对象
环境与需求
- 技术栈:Spring + MongoRepository + JDK 11
- 需求:根据传入的
questionId和answerId,从QuestionDocument的answers列表中返回对应的单个Answer对象(包含answerBody、answerAuthor等完整字段信息)
现有代码
QuestionDocument POJO
@Data @Document(collection = "Questions") @AllArgsConstructor(onConstructor = @__(@Autowired)) @NoArgsConstructor public class QuestionDocument { @Id private String questionId; // 其他字段 private List<Answer> answers; // 其他字段 }
Answer POJO
@Data public class Answer implements Serializable { private String answerId; // 其他字段(如answerBody、answerAuthor等) }
QuestionRepository
@Repository public interface QuestionRepository extends MongoRepository<QuestionDocument, String> { @Query(value = "{ { 'questionId' : ?0 }, { 'answers.$answerId' : ?1 } }") Answer findByQuestionIdAndAnswerId(String questionId, String answerId); }
QuestionServiceImpl 相关代码
public Answer getAnswer(String questionId, String answerId){ return findByQuestionIdAndAnswerId(questionId, answerId); } protected Answer findByQuestionIdAndAnswerId(String questionId, String answerId){ try { return questionRepository.findByQuestionIdAndAnswerId(questionId, answerId); } catch (Exception e) { throw new IllegalArgumentException("There is no answer with this ID."); } }
问题现象
调用接口时响应状态显示成功,但返回的Answer对象所有字段均为null,已确认传入的questionId和answerId是正确的。
已尝试的无效方案
- 创建类似
findByQuestion_Answers_AnswerId(String answerId);的查询方法 - 在
List<Answer> answers上添加@DBRef,并给AnswerPOJO添加@Document(collection = "Answers")和@Id注解,清空数据库重建数据后查询,仍返回null
解决方案
你的@Query写法存在语法错误,MongoDB的查询条件和投影规则需要分开定义,以下是修正后的实现:
1. 修改QuestionRepository
@Repository public interface QuestionRepository extends MongoRepository<QuestionDocument, String> { @Query(value = "{ 'questionId' : ?0, 'answers.answerId' : ?1 }", fields = "{ 'answers.$' : 1, '_id' : 0 }") QuestionDocument findAnswerByQuestionIdAndAnswerId(String questionId, String answerId); }
- 查询条件(value):
{ 'questionId' : ?0, 'answers.answerId' : ?1 }匹配questionId正确且answers列表中存在对应answerId的文档 - 投影规则(fields):
{ 'answers.$' : 1, '_id' : 0 }只返回匹配的单个Answer元素($是MongoDB的位置操作符,指向数组中匹配条件的元素),同时排除默认返回的_id字段
2. 调整Service层逻辑
因为Spring Data MongoDB无法直接将投影后的数组元素映射为单个Answer对象,需要从返回的QuestionDocument中提取目标Answer:
public Answer getAnswer(String questionId, String answerId){ QuestionDocument doc = findAnswerByQuestionIdAndAnswerId(questionId, answerId); if (doc == null || doc.getAnswers() == null || doc.getAnswers().isEmpty()) { throw new IllegalArgumentException("There is no answer with this ID."); } return doc.getAnswers().get(0); } protected QuestionDocument findAnswerByQuestionIdAndAnswerId(String questionId, String answerId){ try { return questionRepository.findAnswerByQuestionIdAndAnswerId(questionId, answerId); } catch (Exception e) { throw new IllegalArgumentException("There is no answer with this ID."); } }
可选:用MongoTemplate实现更灵活的查询
如果希望简化流程,也可以直接使用MongoTemplate进行查询:
@Autowired private MongoTemplate mongoTemplate; public Answer getAnswer(String questionId, String answerId) { Query query = new Query(Criteria.where("questionId").is(questionId) .and("answers.answerId").is(answerId)); query.fields().include("answers.$").exclude("_id"); QuestionDocument doc = mongoTemplate.findOne(query, QuestionDocument.class); if (doc == null || doc.getAnswers() == null || doc.getAnswers().isEmpty()) { throw new IllegalArgumentException("There is no answer with this ID."); } return doc.getAnswers().get(0); }
内容的提问来源于stack exchange,提问作者Tommy A.
相关产品推荐
相关产品推荐

