为什么 Hibernate 在使用 @Fetch(FetchMode.JOIN) 时执行多个 SELECT 查询而不是一个查询
Why does Hibernate execute multiple SELECT queries instead of one when using @Fetch(FetchMode.JOIN)
我有以下查询,我希望在单个 select 请求中 运行:
@NamedQuery(name=Game.GET_GAME_BY_ID1,
query = "SELECT g FROM Game g " +
"JOIN FETCH g.team1 t1 " +
"JOIN FETCH t1.players p1 " +
"JOIN FETCH p1.playerSkill skill1 " +
"where g.id=:id")
问题是所有内容都是通过单独的多个查询获取的。
我只想在一个请求中获取团队和团队的球员以及每个球员的技能。但相反,我有多个 select 查询来获取每个团队、球员、每个球员的统计数据和技能。
以下是与给定注释一起使用的实体:
游戏实体:
public class Game implements Serializable {
private Integer id;
private Integer dayNumber;
private Long date;
private Integer score1;
private Integer score2;
private Team team1;
private Team team2;
....
@ManyToOne(fetch=FetchType.EAGER)
@Fetch(FetchMode.JOIN)
@JoinColumn(name="team_id1")
public Team getTeam1() {
return team1;
}
public void setTeam1(Team team1) {
this.team1 = team1;
}
// uni directional many to one association to Team
@ManyToOne(fetch=FetchType.EAGER)
@Fetch(FetchMode.JOIN)
@JoinColumn(name="team_id2")
public Team getTeam2() {
return team2;
}
public void setTeam2(Team team2) {
this.team2 = team2;
}
}
团队实体:
public class Team implements Serializable {
...
private Set<Player> players;
...
@OneToMany(mappedBy="team", targetEntity=Player.class, fetch=FetchType.LAZY, cascade=CascadeType.ALL)
@Fetch(FetchMode.JOIN)
@OrderBy(value="batOrder, pitRotationNumber ASC")
public Set<Player> getPlayers() {
return players;
}
public void setPlayers(Set<Player> players) {
this.players = players;
}
}
玩家实体:
public class Player implements Serializable {
private PlayerStat playerStats;
private PlayerSkill playerSkill;
...
@OneToOne(mappedBy="player", cascade=CascadeType.ALL)
@Fetch(FetchMode.JOIN)
public PlayerStat getPlayerStats() {
return this.playerStats;
}
public void setPlayerStats(PlayerStat playerStats) {
this.PlayerStats = playerStats;
}
...
@OneToOne(mappedBy="player", fetch=FetchType.LAZY, cascade=CascadeType.ALL)
@Fetch(FetchMode.JOIN)
public PlayerSkill getPlayerSkill() {
return this.playerSkill;
}
public void setPlayerSkill(PlayerSkill playerSkill) {
this.playerSkill = playerSkill;
}
}
你能指出所犯的错误吗?
我需要一个 select 查询来加载游戏,它是团队、团队的球员和每个球员的技能。
编辑 1:
这是 postgresql 日志(其中的一部分),纯 sql 查询:
http://pastebin.com/Fbsvmep6
为简单起见,此问题中更改了表的原始名称,
Game 是 GamelistLeague,Team 是 TeamInfo,有 BatterStats 和 PitcherStats 而不是一个 PlayerStat
日志中的第一个查询是上面这个问题中显示的查询(命名查询),如果我直接在数据库中执行它,returns 所需的一切。
二次查询来自:
@ManyToOne(fetch=FetchType.EAGER)
@Fetch(FetchMode.JOIN)
@JoinColumn(name="team_id2")
public Team getTeam2() {
return team2;
}
因此,您需要:
让所有的联想变得懒惰。默认情况下,all @ManyToOne and @OneToOne associations 是 EAGER,因此最好让它们 LAZY 并且只在查询基础上覆盖获取计划。
删除 @Fetch(FetchMode.JOIN)
,它本质上是一个 EAGER 提取指令。在您的情况下,不仅会获取 team2 属性,还会获取其球员和技能。
您遇到了一个众所周知的问题,a.k.a。 "N+1 selects"。
简而言之,当您 select 一个 parent 实体并且休眠将为与 parent 相关的 child 添加额外的 select 时,就会出现 "N+1 selects" 问题与一对一。因此,如果数据库中有 "N" parent-child 条记录,hibernate 将获取所有 parent 和一条 select,然后将每个 child 分隔为 [=38] =], 总共 N+1 selects.
hibernate 中有两种解决 "N+1" 问题的方法:
1. "Join Fetch" all OneToOne children.
2.启用二级缓存,在OneToOne上使用@Cache注解children.
你的问题是你没有 "join fetch" 所有的 OneToOne children。
您必须 "join fetch" 它们全部,包括可传递的 children(从 children 本身或集合中引用的实体)。
使 OneToOne 变得懒惰(因为默认情况下它是急切的)只是部分解决方案,因为仅当您在 child,但从长远来看,它仍然会使所有 N selects.
我有以下查询,我希望在单个 select 请求中 运行:
@NamedQuery(name=Game.GET_GAME_BY_ID1,
query = "SELECT g FROM Game g " +
"JOIN FETCH g.team1 t1 " +
"JOIN FETCH t1.players p1 " +
"JOIN FETCH p1.playerSkill skill1 " +
"where g.id=:id")
问题是所有内容都是通过单独的多个查询获取的。 我只想在一个请求中获取团队和团队的球员以及每个球员的技能。但相反,我有多个 select 查询来获取每个团队、球员、每个球员的统计数据和技能。
以下是与给定注释一起使用的实体:
游戏实体:
public class Game implements Serializable {
private Integer id;
private Integer dayNumber;
private Long date;
private Integer score1;
private Integer score2;
private Team team1;
private Team team2;
....
@ManyToOne(fetch=FetchType.EAGER)
@Fetch(FetchMode.JOIN)
@JoinColumn(name="team_id1")
public Team getTeam1() {
return team1;
}
public void setTeam1(Team team1) {
this.team1 = team1;
}
// uni directional many to one association to Team
@ManyToOne(fetch=FetchType.EAGER)
@Fetch(FetchMode.JOIN)
@JoinColumn(name="team_id2")
public Team getTeam2() {
return team2;
}
public void setTeam2(Team team2) {
this.team2 = team2;
}
}
团队实体:
public class Team implements Serializable {
...
private Set<Player> players;
...
@OneToMany(mappedBy="team", targetEntity=Player.class, fetch=FetchType.LAZY, cascade=CascadeType.ALL)
@Fetch(FetchMode.JOIN)
@OrderBy(value="batOrder, pitRotationNumber ASC")
public Set<Player> getPlayers() {
return players;
}
public void setPlayers(Set<Player> players) {
this.players = players;
}
}
玩家实体:
public class Player implements Serializable {
private PlayerStat playerStats;
private PlayerSkill playerSkill;
...
@OneToOne(mappedBy="player", cascade=CascadeType.ALL)
@Fetch(FetchMode.JOIN)
public PlayerStat getPlayerStats() {
return this.playerStats;
}
public void setPlayerStats(PlayerStat playerStats) {
this.PlayerStats = playerStats;
}
...
@OneToOne(mappedBy="player", fetch=FetchType.LAZY, cascade=CascadeType.ALL)
@Fetch(FetchMode.JOIN)
public PlayerSkill getPlayerSkill() {
return this.playerSkill;
}
public void setPlayerSkill(PlayerSkill playerSkill) {
this.playerSkill = playerSkill;
}
}
你能指出所犯的错误吗? 我需要一个 select 查询来加载游戏,它是团队、团队的球员和每个球员的技能。
编辑 1: 这是 postgresql 日志(其中的一部分),纯 sql 查询: http://pastebin.com/Fbsvmep6
为简单起见,此问题中更改了表的原始名称, Game 是 GamelistLeague,Team 是 TeamInfo,有 BatterStats 和 PitcherStats 而不是一个 PlayerStat
日志中的第一个查询是上面这个问题中显示的查询(命名查询),如果我直接在数据库中执行它,returns 所需的一切。
二次查询来自:
@ManyToOne(fetch=FetchType.EAGER)
@Fetch(FetchMode.JOIN)
@JoinColumn(name="team_id2")
public Team getTeam2() {
return team2;
}
因此,您需要:
让所有的联想变得懒惰。默认情况下,all @ManyToOne and @OneToOne associations 是 EAGER,因此最好让它们 LAZY 并且只在查询基础上覆盖获取计划。
删除
@Fetch(FetchMode.JOIN)
,它本质上是一个 EAGER 提取指令。在您的情况下,不仅会获取 team2 属性,还会获取其球员和技能。
您遇到了一个众所周知的问题,a.k.a。 "N+1 selects"。
简而言之,当您 select 一个 parent 实体并且休眠将为与 parent 相关的 child 添加额外的 select 时,就会出现 "N+1 selects" 问题与一对一。因此,如果数据库中有 "N" parent-child 条记录,hibernate 将获取所有 parent 和一条 select,然后将每个 child 分隔为 [=38] =], 总共 N+1 selects.
hibernate 中有两种解决 "N+1" 问题的方法:
1. "Join Fetch" all OneToOne children.
2.启用二级缓存,在OneToOne上使用@Cache注解children.
你的问题是你没有 "join fetch" 所有的 OneToOne children。 您必须 "join fetch" 它们全部,包括可传递的 children(从 children 本身或集合中引用的实体)。
使 OneToOne 变得懒惰(因为默认情况下它是急切的)只是部分解决方案,因为仅当您在 child,但从长远来看,它仍然会使所有 N selects.