Java 为什么Hibernate在使用@Fetch(FetchMode.JOIN)时执行多个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

我有以下查询,我希望在一个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;
    }
}
团队实体:

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/任意的设计决策-这种行为有具体的原因吗?