- 部屋(Room)テーブルと備品(Equipment)テーブルがあり、それぞれ1対多のリレーションがあるとする。
- JPQLでの結合をしてみる。
package com.example.demo.repository;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import com.example.demo.entity.Room;
public interface RoomRepository extends JpaRepository<Room, Integer> {
@Query("SELECT r FROM Room r LEFT OUTER JOIN r.equipments")
List<Room> find();
}
独特な構文だが、この書き方の場合、RoomテーブルをSELECTした後に、Equipmentテーブルを1件ずつ取得する、いわゆるN+1問題が発生する。
①Roomテーブルのselect
select
r1_0.room_id,
r1_0.invalid,
r1_0.room_name
from
room r1_0 left join equipment e1_0 on r1_0.room_id=e1_0.room_id
②①で取得したRoomテーブルのレコードのうち、
Equipmentテーブルに紐づくレコードがある場合、都度selectを行う。
select
e1_0.room_id,
e1_0.equipment_id,
e1_0.equipment_name,
e1_0.invalid
from
equipment e1_0
where
e1_0.room_id=?
binding parameter [1] as [INTEGER] - [1]
select
e1_0.room_id,
e1_0.equipment_id,
e1_0.equipment_name,
e1_0.invalid
from
equipment e1_0
where
e1_0.room_id=?
binding parameter [1] as [INTEGER] - [2]
select
e1_0.room_id,
e1_0.equipment_id,
e1_0.equipment_name,
e1_0.invalid
from
equipment e1_0
where
e1_0.room_id=?
binding parameter [1] as [INTEGER] - [3k]
- これを防ぐにはJOIN FETCHを行う。
package com.example.demo.repository;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import com.example.demo.entity.Room;
public interface RoomRepository extends JpaRepository<Room, Integer> {
@Query("SELECT r FROM Room r LEFT OUTER JOIN FETCH r.equipments")
List<Room> find();
}
- 実行結果を見ると一度のクエリで結合情報を取得できていた。
select
r1_0.room_id,
e1_0.room_id,
e1_0.equipment_id,
e1_0.equipment_name,
e1_0.invalid,
r1_0.invalid,
r1_0.room_name
from
room r1_0 left join equipment e1_0
on r1_0.room_id=e1_0.room_id
- よほどの理由がない限り、JPQLで結合を行うときはFETCHをつけること。
コメント