Spring+Ajax获取Answer列表仅首个完整其余为ID问题排查
看起来你遇到的问题是双向实体关联导致的JSON序列化循环引用处理问题,和控制器逻辑关系不大,核心问题出在实体类的序列化配置与数据获取方式上。
为什么会出现这个现象?
你的Question和Answer是双向关联的:Question包含List<Answer>,Answer又持有Question引用。加上你给每个实体都配置了@JsonIdentityInfo,当Spring默认的Jackson序列化工具处理List<Answer>时:
- 第一个
Answer对象会被完整序列化,同时会把它关联的Question也序列化出来; - 而这个
Question对象里又包含了整个Answer列表,为了避免无限循环序列化,Jackson会把后续重复出现的Answer对象替换成它们的answer_id(也就是你配置的@JsonIdentityInfo的property字段); - 最终就出现了你看到的:第一个
Answer完整,其余只有ID的情况。
解决方案(按推荐程度排序)
1. 忽略Answer中的Question关联(最简单有效)
既然你在前端只需要Answer的内容(答案文本、是否正确),不需要关联的Question信息,直接在Answer的question字段上加上@JsonIgnore,让序列化时跳过这个字段:
@Entity @JsonIdentityInfo( generator = ObjectIdGenerators.PropertyGenerator.class, property = "answer_id") public class Answer { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private int answer_id; private String answer; private boolean corect; @ManyToOne @JsonIgnore // 新增这个注解 private Question question; }
这样序列化出来的每个Answer都是完整的,不会携带Question引用,自然也就不会触发循环引用的ID替换逻辑。
2. 直接查询Answer实体,避免加载Question关联
修改你的findByQuestionId方法,直接从Answer表查询,而不是通过Question的关联获取,这样可以避免加载不必要的Question对象:
@Override public List<Answer> findByQuestionId(int question_id) { Session session = sessionFactory.openSession(); session.beginTransaction(); // 直接查询属于该question_id的所有Answer List<Answer> answers = session.createQuery( "SELECT a FROM Answer a WHERE a.question.id = :questionId", Answer.class) .setParameter("questionId", question_id) .getResultList(); session.getTransaction().commit(); session.close(); return answers; }
这种方式获取的Answer列表,不会主动加载关联的Question(因为@ManyToOne默认是懒加载),序列化时也就不会触发循环引用问题。
3. 调整Jackson的循环引用处理策略
如果你确实需要保留双向关联的序列化,可以去掉@JsonIdentityInfo,改用@JsonManagedReference和@JsonBackReference来标记双向关联:
- 在
Question的answers字段上加上@JsonManagedReference(主动序列化的一端) - 在
Answer的question字段上加上@JsonBackReference(被忽略序列化的一端)
示例:
// Question类 @OneToMany(mappedBy = "question", fetch = FetchType.EAGER, cascade = CascadeType.ALL) @Fetch(value = FetchMode.SUBSELECT) @JsonManagedReference // 新增 private List<Answer> answers; // Answer类 @ManyToOne @JsonBackReference // 新增 private Question question;
这种方式也能解决循环引用问题,同时保留实体间的关联关系序列化(但前端依然不需要Question信息的话,第一种方法更直接)。
额外检查
确认你的Ajax代码中answer.answer是正确的字段名:你的Answer实体里的答案文本字段是private String answer;,所以answer.answer是正确的,可以通过console.log(answers)在浏览器控制台查看序列化后的JSON结构,确认字段名是否匹配。
内容的提问来源于stack exchange,提问作者Ovidiu G

