当前位置: 首页 > 知识库问答 >
问题:

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

赵开诚
2023-03-14

我希望在一个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")

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

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

游戏实体:

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

为了简单起见,在这个问题中更改了表的原始名称,游戏是GamelistLeague,团队是TeamInfo,并且有BatterStats和PitcherStats,而不是一个PlayerStat

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

共有2个答案

东郭兴学
2023-03-14

次要查询来自:

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

所以,你需要:

>

  • 让所有的联想变得懒惰。默认情况下,所有@ManyToOne和@OneToOne关联都是渴望的,所以最好让它们变懒,只在查询的基础上重写获取计划。

    删除@Fetch(FetchMode.JOIN),它本质上是一个EAGER读取指令。在你的例子中,不仅仅是团队2的属性被获取,它的玩家和技能也是如此。

  • 王飞英
    2023-03-14

    你正在经历一个众所周知的问题,又名“N 1选择”。简而言之,当您选择父实体时,会出现“N 1选择”问题,Hibernate将为与OneToOne父实体相关的子实体进行额外选择。因此,如果您在数据库中有N个父子记录,hibernate将使用一个选择获取所有父母,然后在分开的选择中获取每个孩子,总共有N个选择。
    hibernate中的“N 1”问题有两种方法:
    1。"加入Fetch"所有OneToOne儿童。
    2.启用二级缓存,并在OneToOne子级上使用@缓存注释。

    你的问题是你没有“加入”所有的OneToOne孩子。您必须“连接”它们,包括可传递的子对象(从子对象本身或集合中引用的实体)。

    让OneToOne变得懒惰(因为默认情况下它是渴望的)只是部分解决方案,因为Hibernate只会在您访问孩子的一些getter时为孩子进行选择,但从长远来看,它仍然会进行所有的N个选择。

     类似资料:
    • 考虑和<代码>雇员< /代码>和<代码>地址< /代码>关系。在和之间有一对一的映射。以下是模型: 现在,当我执行以下HQL查询时,它会在内部生成两个查询。一个取员工,另一个取地址。 由Hibernate生成的SQL查询 因为我使用的是,所以我希望Hibernate只执行一个带有JOIN的查询,一次性获取员工和地址数据。 你知道为什么它要执行两个查询吗?我如何让Hibernate使用join只执行

    • 问题陈述:在JPA hibernate中,我执行了一个方法

    • 我正在使用spring boot开发REST应用程序,并试图优化查询的性能。我目前正在使用存储库中的,这会导致性能问题。代码如下: 个人实体 密码实体 角色实体 人员存储库 当我做一个时,在人表中的每一行都会触发选择查询,以获取密码和角色。我知道我可以在存储库中使用注释和来强制生成单个查询,但是我想知道是否有其他方法可以这样做。我正在寻找我们可以在实体级别上做的事情来减少查询。 使用Spring

    • 问题内容: 我有以下查询,希望在单个选择请求中运行: 问题在于,所有内容都是通过单独的多个查询获取的。我只希望在单个请求中获取团队和团队的球员以及每个球员的技能。但是取而代之的是,我有多个选择查询来获取每个球队,每个球员,每个球员的数据和技能。 以下是带有给定注释的实体: 游戏实体: 团队实体: 玩家实体: 您能指出犯下的错误吗?我需要一个选择查询来加载游戏,它是团队,团队的球员和每个球员的技能。

    • 需求:从一个用户界面,我得到一个数据库表的选定列表。这些表中的数据将被提取并存储在文件位置。我们希望使用NiFi Rest API,因为有定制UI的需求。因此,我们使用REST API调用NiFi处理器。 问题是如何在循环中为在分隔字符串中接收的每个表名调用ExecuteSQL。 如果需要更多的信息,请让我知道。

    • 问题内容: 我要查询所有10个表以从中获取用户ID,并将所有用户ID加载到HashSet中,以便可以拥有唯一的用户ID。 截至目前,它是按顺序进行的。我们转到一个表,并从中提取所有user_id并将其加载到哈希集中,然后将其存储在第二个和第三个表中并继续运行。 有什么方法可以使这个多线程的,以便他们为每个表并行地从我的表中获取数据?最后,我需要哈希集,该哈希集应具有所有10个表中的所有唯一用户ID