Spring Data JPA一对多关联场景下Transient字段返回错误值问题
问题核心原因
你当前的逻辑存在两个核心错误:
- 统计符合条件的单词数量的代码完全错误:
stream.map(w -> w.getStatusRepeating()).count()统计的是words集合的总元素个数,不管getStatusRepeating()返回的是true还是false,只要集合里有元素,返回的计数就等于单词总数,只要集合非空就会返回true,这是你得到错误结果的根本原因。 - 多余字段+序列化重复调用导致两次查询:
Deck中定义了showWordsToRevise字段,同时提供了自定义逻辑的getter方法,Jackson序列化时会两次触发getter调用,加上@OneToMany默认是懒加载策略,每次访问words集合都会触发关联查询,所以会出现两次SQL查询。
解决方案
1. 修正统计逻辑
直接用Stream的anyMatch方法替代计数逻辑,性能更高且逻辑准确:
public Boolean getShowWordsToRevise() { // 只要存在一个符合复习条件的单词就返回true,否则返回false return this.words.stream() .anyMatch(w -> Boolean.TRUE.equals(w.getStatusRepeating())); }
如果你需要保留计数打印,可以改为:
public Boolean getShowWordsToRevise() { long count = this.words.stream() .filter(w -> Boolean.TRUE.equals(w.getStatusRepeating())) .count(); System.out.println(count + " words to revise" + java.time.LocalDateTime.now()); return count > 0; }
2. 移除多余字段避免重复调用
删除Deck中定义的showWordsToRevise字段,该字段是@Transient修饰不需要持久化,且getter是实时计算不需要存储值,保留字段只会导致Jackson重复读取触发多次查询。删除后仅保留getShowWordsToRevise方法即可。
3. 优化关联查询避免重复SQL
如果每次查询Deck都需要用到关联的words集合,可以将关联策略改为立即加载,避免懒加载触发多次查询:
@JsonManagedReference @OneToMany(mappedBy = "deck", cascade = CascadeType.ALL, fetch = FetchType.EAGER) private List<Word> words;
如果不想全局修改加载策略,也可以在查询Deck的DAO层方法上添加@EntityGraph注解一次性加载关联的words集合:
@EntityGraph(attributePaths = "words") Optional<Deck> findById(Long id);
可选优化:空指针防护
Word类的getStatusRepeating方法中如果last_checked为null会抛出空指针异常,建议添加空判断:
public Boolean getStatusRepeating() { if (last_checked == null) { return false; } int days = Period.between(this.last_checked,LocalDate.now()).getDays(); if (this.wordGroup == WordGroup.first && days > 1) return true; if (this.wordGroup == WordGroup.second && days > 2) return true; return this.wordGroup == WordGroup.third && days > 7; }
内容的提问来源于stack exchange,提问作者user321
相关产品推荐
相关产品推荐

