単一の選択要求で実行されると予想される次のクエリがあります。
@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")
問題は、すべてが個別の複数のクエリによってフェッチされることです。チームとチームのプレイヤーと各プレイヤーのスキルのみを1回のリクエストで取得する必要があります。しかし、代わりに、各チーム、プレーヤー、各プレーヤーの統計とスキルを取得するための複数の選択クエリがあります。
以下は、指定された注釈で使用されるエンティティです。
ゲームエンティティ:
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つの選択クエリが必要です。
EDIT 1:ここはpostgresqlログ(その一部)、純粋なSQLクエリです: http://Pastebin.com/Fbsvmep6
この質問では、簡単にするためにテーブルの元の名前が変更されています。ゲームはGamelistLeague、チームはTeamInfo、そしてPlayerStatの代わりにBatterStatsとPitcherStatsがあります
ログからの最初のクエリは、上記のこの質問に示されているクエリ(名前付きクエリ)です。データベースで直接実行すると、必要に応じてすべてが返されます。
「N + 1選択」というよく知られた問題が発生しています。つまり、親エンティティを選択すると、「N + 1選択」問題が発生し、HibernateはOneToOneを使用して親に関連する子を追加選択します。したがって、データベースに「N」個の親子レコードがある場合、hibernateはすべての親を1回の選択で取得し、次に各子を個別の選択で取得して、合計N + 1回の選択を行います。
Hibernateの「N + 1」問題には2つのアプローチがあります。
1。 「Join Fetch」all OneToOneの子。
2。 2次キャッシュを有効にし、OneToOneの子で@Cacheアノテーションを使用します。
問題は、すべてのOneToOne子を「結合フェッチ」しなかったことです。推移的な子(子自体またはコレクション内から参照されるエンティティ)を含め、それらすべてを「結合フェッチ」する必要があります。
OneToOneをレイジーにする(デフォルトでは熱心なため)のは部分的な解決策にすぎません。なぜなら、hibernateは子のゲッターにアクセスするときにのみ子を選択しますが、長期的にはN個すべてを選択するからです。
二次クエリは以下から来ています。
_@ManyToOne(fetch=FetchType.EAGER)
@Fetch(FetchMode.JOIN)
@JoinColumn(name="team_id2")
public Team getTeam2() {
return team2;
}
_
だから、あなたはする必要があります:
すべての関連付けをLAZYにします。デフォルトでは、 すべての@ManyToOneおよび@OneToOneの関連付け はEAGERであるため、LAZYにしてクエリベースでのみフェッチプランをオーバーライドする方が適切です。
本質的にEAGERフェッチディレクティブである@Fetch(FetchMode.JOIN)
を削除します。あなたの場合、team2プロパティだけでなく、そのプレイヤーとスキルも取得されます。