简体   繁体   English

为什么 Hibernate 在使用 @Fetch(FetchMode.JOIN) 时执行多个 SELECT 查询而不是一个

[英]Why does Hibernate execute multiple SELECT queries instead of one when using @Fetch(FetchMode.JOIN)

I've got the following query which I expect to run in a single select request:我有以下查询,希望在单个选择请求中运行:

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

The problem is that everything is fetched by separate multiple queries.问题是所有内容都是通过单独的多个查询获取的。 I want only Team and team's players and each player's skills to be fetched in a single request.我只想在单个请求中获取团队和团队的球员以及每个球员的技能。 But instead I've got multiple select queries for fetching each team, player, each player's stats and skills.但相反,我有多个选择查询来获取每个团队、球员、每个球员的统计数据和技能。

Here are entities used with annotations given:以下是与给定注释一起使用的实体:

Game Entity:游戏实体:

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;
    }
}

Team Entity:团队实体:

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;
    }
}

Player Entity:玩家实体:

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;
    }
}

Could you point on the mistakes made?你能指出所犯的错误吗? I need one select query to load game, it's teams, team's players and each player's skills.我需要一个选择查询来加载游戏,它是团队、团队的球员和每个球员的技能。

EDIT 1: here is postgresql log (some part of it), pure sql queries: http://pastebin.com/Fbsvmep6编辑 1:这里是 postgresql 日志(它的一部分),纯 sql 查询: http : //pastebin.com/Fbsvmep6

Original names of tables are changed in this question for simplicity, Game is GamelistLeague, Team is TeamInfo, and there are BatterStats and PitcherStats instead of one PlayerStat为简单起见,在此问题中更改了表格的原始名称,Game 是 GamelistLeague,Team 是 TeamInfo,并且有 BatterStats 和 PitcherStats 而不是一个 PlayerStat

The first query from the logs is the one shown in this question above (named query) which, if I execute it directly in database, returns everything as needed.日志中的第一个查询是上面这个问题中显示的查询(命名查询),如果我直接在数据库中执行它,则会根据需要返回所有内容。

You are experiencing a well known problem, aka the "N+1 selects".您遇到了一个众所周知的问题,也就是“N+1 选择”。 In short, the "N+1 selects" problem occurs when you select a parent entity and hibernate will make additional select for a child related to the parent with OneToOne.简而言之,当您选择父实体时,会出现“N+1 选择”问题,并且休眠将使用 OneToOne 为与父实体相关的子实体进行额外的选择。 So if you have "N" parent-child records in the database, hibernate will get all parents with one select and then get each child in separated select, making total N+1 selects.因此,如果您在数据库中有“N”条父子记录,hibernate 将获得所有父级的一个选择,然后将每个子级置于单独的选择中,总共 N+1 个选择。
There are two approaches for "N+1" problem in hibernate: Hibernate 中有两种解决“N+1”问题的方法:
1. "Join Fetch" all OneToOne children. 1.“加入获取”所有一对一孩子。
2. Enable the second level cache and use @Cache annotation on the OneToOne children. 2.启用二级缓存,并在OneToOne子节点上使用@Cache注解。

Your problem is that you didn't "join fetch" all of the OneToOne children.您的问题是您没有“加入获取”所有 OneToOne 孩子。 You must "join fetch" them all, including the transitive children (entities referenced from children themselves, or within the collection).您必须“加入获取”所有这些,包括可传递的子项(从子项本身或集合中引用的实体)。

Making OneToOne lazy (because its eager by default) is only partial solution, because hibernate will make a select for a child only when you access some getter on the child, but in long term it will still make all the N selects.使 OneToOne 懒惰(因为默认情况下它是渴望的)只是部分解决方案,因为只有当您访问孩子的某些 getter 时,hibernate 才会为孩子进行选择,但从长远来看,它仍然会进行所有 N 次选择。

The secondary queries come from:次要查询来自:

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

So, you need to:所以,你需要:

  1. Make all associations LAZY.使所有关联懒惰。 By default, all @ManyToOne and @OneToOne associations are EAGER, so it's better to have them LAZY and only override the fetch plan on a query basis.默认情况下,所有 @ManyToOne 和 @OneToOne 关联都是 EAGER,所以最好让它们 LAZY 并且只在查询的基础上覆盖获取计划。

  2. Remove the @Fetch(FetchMode.JOIN) , which is essentially an EAGER fetch directive.删除@Fetch(FetchMode.JOIN) ,它本质上是一个 EAGER fetch 指令。 In your case, not just the team2 property is fetched, but its players and skills as well.在您的情况下,不仅获取了team2属性,还获取了它的玩家和技能。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 使用@Fetch(FetchMode.JOIN)时,JPA + Hibernate的查询问题太多 - Too many queries problem with JPA + Hibernate even when using @Fetch(FetchMode.JOIN) 为什么Hibernate有时会忽略FetchMode.JOIN? - Why Hibernate sometimes ignores FetchMode.JOIN? Hibernate FetchMode.JOIN 与一对多映射 - Hibernate FetchMode.JOIN with one to many mapping Hibernate @OneToOne为什么执行多个选择查询而不是一个? - Why does Hibernate @OneToOne execute multiple select queries instead of one? Hibernate中的@Fetch(FetchMode.JOIN)违反了FetchType.LAZY - @Fetch(FetchMode.JOIN) in Hibernate violates the FetchType.LAZY 网格产生太多的休眠查询-FetchMode.JOIN不起作用 - Grid produces too many hibernate queries - FetchMode.JOIN doesn't work 休眠条件 OneToOne FetchType.Eager FetchMode.Join 执行不必要的查询 - Hibernate criteria OneToOne FetchType.Eager FetchMode.Join execute unnecessary queryes 使用FetchMode.JOIN的Hibernate OneToOne(optional = true)尝试重新选择空值 - Hibernate OneToOne(optional=true) with FetchMode.JOIN try to re-select null values Java Spring JPA FetchMode.JOIN不使用JOIN - Java Spring JPA FetchMode.JOIN not using JOIN JPA Hibernate 非主键的 FetchType.EAGER 和 FetchMode.JOIN - JPA Hibernate FetchType.EAGER and FetchMode.JOIN for non primary key
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM