Java 为什么Hibernate在使用@Fetch(FetchMode.JOIN)时执行多个SELECT查询而不是一个查询
我有以下查询,我希望在一个select请求中运行:Java 为什么Hibernate在使用@Fetch(FetchMode.JOIN)时执行多个SELECT查询而不是一个查询,java,hibernate,jpa,hql,fetch,Java,Hibernate,Jpa,Hql,Fetch,我有以下查询,我希望在一个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
@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;
}
}
团队实体:
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;
}
}
你能指出所犯的错误吗?
我需要一个选择查询来加载游戏,它是团队、团队的玩家和每个玩家的技能
编辑1:
以下是postgresql日志(部分日志),纯sql查询:
为了简单起见,在这个问题中更改了表的原始名称,
游戏是GamelistLeague,团队是TeamInfo,有击球手统计和投手统计,而不是一个PlayerStat
日志中的第一个查询是上面这个问题中显示的查询(命名查询),如果我直接在数据库中执行它,它将根据需要返回所有内容。第二个查询来自:
@ManyToOne(fetch=FetchType.EAGER)
@Fetch(FetchMode.JOIN)
@JoinColumn(name="team_id2")
public Team getTeam2() {
return team2;
}
因此,您需要:
@Fetch(FetchMode.JOIN)
,它本质上是一个急切的Fetch指令。在您的情况下,不仅获取team2属性,还获取其玩家和技能您遇到了一个众所周知的问题,即“N+1选择”。 简言之,“N+1选择”问题发生在您选择父实体时,hibernate将使用OneTONE为与父实体相关的子实体进行额外选择。因此,如果数据库中有“N”个父子记录,hibernate将通过一次选择获取所有父项,然后在单独的选择中获取每个子项,使总选择数为N+1。
对于hibernate中的“N+1”问题,有两种方法:
1.“加入获取”所有子项。
2.启用二级缓存并在OneTONE子级上使用@cache注释。
你的问题是你没有“加入”所有的OneToOne孩子。 您必须“连接”它们,包括可传递的子对象(从子对象本身或集合中引用的实体)
使OneToOne变为懒惰(因为默认情况下它是急切的)只是部分解决方案,因为hibernate仅在您访问某个孩子的getter时才会为该孩子进行选择,但从长远来看,它仍然会进行所有N次选择。您能发布正在执行的实际SQL吗?如果关闭延迟抓取,会发生什么?@chrylis请查看编辑。我可以尝试关闭延迟抓取,但稍后,尽管我需要将它设置为其他查询的延迟,而这些查询不应该被急切地抓取。Hibernate。。。自2001年以来,让聪明人执行愚蠢的查询。也许是时候抛弃Hibernate臃肿软件,转而使用:)@LanceJava哈哈哈:我只是想对LanceJava说点什么。我要放弃冬眠,用mybatis。天哪,hibernate真让我蠢透了。我按照你的建议,删除了所有FetchMode.JOIN并添加了LAZY,但我只注意到两个变化:1。团队已加载,但未完成不同的选择,2。所有玩家在没有单独选择的情况下加载。这是很好的,但对于每个玩家来说,它的数据和技能仍然在加载。我已经检查过好几次了,没有对它们的即时加载,也没有JOIN-fetchMode。您还需要显式地为OneToOne和ManyToOne设置LAZY。然后在您的查询中使用join fetch。即使我不需要加载这些实体,我也必须执行join fetch?您只需要在需要它们时加入并获取它们。如果不必加载它们,只需加载父实体即可。在这种情况下,所有惰性关联将仅在访问时初始化。除非您访问惰性关联,否则它们将永远不会触发二次选择。抱歉,它似乎仍然不起作用。我只是用那个查询进行了测试。之前,由于之前的抓取可能已成功,因此无法加载。不确定。但是,即使在您在回答代码中提到的更改之后,它仍然会使用select request GOTED to db加载团队。@在处理延迟回迁时,SubSelect也是一个很好的选项。完美答案!如果实体具有@OneToOne字段,则必须在查询中提取这些字段。即使“@OneToOne”字段被标记为FetchType.LAZY,调用“@Query”时也会被认为是急切的:听起来像是一个bug/任意的设计决策-这种行为有具体的原因吗?