Hibernate正在做多个select请求而不是一个(使用join fetch)

时间:2015-01-14 04:14:06

标签: java hibernate jpa hql fetch

我有以下查询,我希望在单个选择请求中运行:

@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")

问题是所有内容都是由单独的多个查询提取的。 我只希望在一个请求中获取团队和团队的玩家以及每个玩家的技能。但相反,我有多个选择查询来获取每个球队,球员,每个球员的统计数据和技能。

以下是与注释一起使用的实体:

游戏实体:

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查询: http://pastebin.com/Fbsvmep6

为简单起见,在此问题中更改了表的原始名称, Game是GamelistLeague,Team是TeamInfo,有BatterStats和PitcherStats而不是一个PlayerStat

日志中的第一个查询是上面这个问题中显示的查询(命名查询),如果我直接在数据库中执行,则根据需要返回所有内容。

2 个答案:

答案 0 :(得分:25)

您遇到了一个众所周知的问题,例如,&#34; N + 1选择&#34;。 简而言之,&#34; N + 1选择&#34;选择父实体时会出现问题,而hibernate将使用OneToOne为与父级相关的子项进行额外选择。所以如果你有&#34; N&#34;在数据库中的父子记录,hibernate会让所有父母一个选择,然后让每个孩子分开选择,使总N + 1选择。
&#34; N + 1&#34;有两种方法。休眠问题:
1.&#34;加入Fetch&#34; 所有 OneToOne儿童。
2.启用二级缓存并在OneToOne子级上使用@Cache批注。

你的问题是你没有加入fetch&#34;所有的OneToOne孩子。 你必须&#34;加入fetch&#34;它们全部,包括传递子(从孩子自己引用的实体,或在集合中引用的实体)。

使OneToOne懒惰(因为它默认情况下急切)只是部分解决方案,因为只有当你访问孩子的某个getter时,hibernate才会为孩子做一个选择,但从长远来看,它仍会进行所有N个选择。

答案 1 :(得分:4)

辅助查询来自:

@ManyToOne(fetch=FetchType.EAGER)
@Fetch(FetchMode.JOIN)
@JoinColumn(name="team_id2")
public Team getTeam2() {
    return team2;
}

所以,你需要:

  1. 使所有协会LAZY。默认情况下,all @ManyToOne and @OneToOne associations是EAGER,因此最好将它们设置为LAZY并仅在查询的基础上覆盖获取计划。

  2. 删除@Fetch(FetchMode.JOIN),它本质上是一个EAGER fetch指令。在您的情况下,不仅提取 team2 属性,而且还提取其玩家和技能。