Hibernate查询返回重复结果:三表关联命名查询异常求助
我之前也遇到过类似的情况——原生SQL查出来结果正常,但用JPA命名查询就返回重复数据,大概率是实体映射或者查询配置的问题,给你几个排查和解决的方向:
1. 检查PlayerMatch实体的主键定义
player_match表的主键应该是复合主键(match_id, team_id, player_id),毕竟一个球员在单场比赛里只会属于一支球队,这三个字段组合起来才能唯一标识一条记录。如果你的实体没正确定义复合主键,JPA没法区分不同的PlayerMatch,自然会返回重复结果。
解决方法:
- 先定义一个可嵌入的复合主键类:
@Embeddable public class PlayerMatchId implements Serializable { private Long matchId; private Long teamId; private Long playerId; // 必须实现equals和hashCode,基于三个主键字段 @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; PlayerMatchId that = (PlayerMatchId) o; return Objects.equals(matchId, that.matchId) && Objects.equals(teamId, that.teamId) && Objects.equals(playerId, that.playerId); } @Override public int hashCode() { return Objects.hash(matchId, teamId, playerId); } // 别忘了添加getter和setter } - 在PlayerMatch实体中关联这个复合主键:
@Entity @Table(name = "player_match") public class PlayerMatch { @EmbeddedId private PlayerMatchId id; @ManyToOne(fetch = FetchType.LAZY) @MapsId("teamId") // 关联到复合主键中的teamId字段 @JoinColumn(name = "team_id") private Team team; @ManyToOne(fetch = FetchType.LAZY) @MapsId("playerId") // 关联到复合主键中的playerId字段 @JoinColumn(name = "player_id") private Player player; // 其他字段和业务方法 }
2. 优化命名查询,避免笛卡尔积引发的重复
虽然你的原生SQL是inner join,但如果用JPQL写命名查询时没处理好关联逻辑,或者用了EAGER抓取策略,可能会因为关联数据的笛卡尔积导致实体被重复加载。可以试试这两种方式:
方式一:在JPQL中添加
DISTINCT关键字@NamedQuery( name = "PlayerMatch.findByMatchId", query = "SELECT DISTINCT pm FROM PlayerMatch pm " + "JOIN pm.team t JOIN pm.player p " + "WHERE pm.id.matchId = :matchId" )这里用的是JPQL语法,关联的是实体的属性而非数据库表字段,记得和你的实体映射对应上。
方式二:用原生SQL命名查询并指定结果类
如果更习惯原生SQL,可以直接复用你验证过的查询语句,同时指定结果映射到PlayerMatch实体:@NamedNativeQuery( name = "PlayerMatch.findByMatchIdNative", query = "SELECT DISTINCT M.* FROM player_match M " + "INNER JOIN team T ON M.team_id = T.team_id " + "INNER JOIN player P ON P.player_id = M.player_id " + "WHERE M.match_id = :matchId", resultClass = PlayerMatch.class )
3. 确认equals和hashCode方法的正确性
如果PlayerMatch实体(或者复合主键类)的equals()和hashCode()方法没正确实现,JPA在处理集合时无法区分不同实体,也会导致重复结果。一定要确保这两个方法是基于实体的唯一标识(也就是复合主键)来实现的。
4. 排查结果处理逻辑
有时候问题不是出在查询本身,而是后续的结果处理。比如你用List<PlayerMatch>接收结果后,代码里不小心重复添加了元素,或者没做去重处理。可以在查询后打印每个实体的主键值,确认是JPA返回了重复,还是自己的处理逻辑出了问题。
内容的提问来源于stack exchange,提问作者Vipul Behl

