조인의 종류
- 먼저 INNER 조인과 OUTER 조인을 비교해보자
- INNER JOIN : 공통적인 부분만 select 한다.
- LEFT OUTER JOIN 조인 기준으로 왼쪽에 있는 데이터를 전부 select 한다.
- 이제 JPQL 에서는 join을 어떻게 처리하는지 알아보자.
- 내부 조인
- SELECT m FROM Member m [INNER] JOIN m.team t
- 이 경우 member는 있고 팀이없으면 해당 데이터가 나오지 않는다.
- 내부 조인
- 외부 조인
- SELECT m FROM Member m LEFT [OUTER] JOIN m.team t
- member 를 기준으로 team 을 붙인다. -> 테이블의 가로[컬럼들]가 넓어진다.
- 이 경우는 member는 있고 팀이 없어도 데이터가 나온다. -> team 은 null로 나온다.
- 다시말해 member 가 기준이므로 member의 행은 모두 표현되어야 하고, 몇몇 team 에 관련된 컬럼이 null 일 수 있다.
- full outer join 의 경우, left join, right join 한 결과를 모두 가져오는 것과 유사하다.
- cross join 의 경우, on 절 없이 모든 경우의 수를 모두 결합해서 가져온다.
- 만약 죄측 테이블의 행이 3개고 우측 테이블의 행이 4개면 총 3 * 4 = 12 개의 행이 반환된다.
- 세타 조인
- SELECT count(m) from Member m, Team t where m.username = t.name
- 연관관계가 없는 것도 곱하기 해서 다 가져올 수 있다. (cross join)
- ON 절을 활용한 조인 (JPA 2.1 부터 지원)
- 조인 대상 필터링
- SELECT m, t FROM Member m LEFT JOIN m.team t on t.name = 'A'
- 이렇게하면 팀 이름이 'A'인 팀만 LEFT JOIN 한다.
- 연관관계 없는 엔티티 외부 조인 (hibernate 5.1 부터 지원)
- SELECT m, t FROM Member m LEFT JOIN Team t on m.username = t.name
- 조인 대상 필터링
Fetch 란?
- 애플리케이션이 DB로부터 데이터를 가져오는 것을 말한다.
- DB와 통신하여 데이터를 읽는 것에는 큰 비용이 소모되기 때문에, 똑똑하게 가져오는 전략이 필요하다.
- Fetch 전략
- eager: 프로그램 코드가 쿼리를 날리는 시점에 데이터를 즉시 가져오기
- ex:selecta.idfromAainnerjoinBbona.b_id=b.id (b를보지 않았지만 일단 다가져옴)
- lazy: 가져오려는 데이터를 애플리케이션에서 실제로 접근할 때 가져오기
- ex: select a.id from A; (select b from B b where b.id = ?)
- 더 빠르고 경제적인 쿼리 (적절히만 사용한다면)
- 잘못 사용하면 데이터 접근 에러 (ex: LazyInitializationException)
- eager: 프로그램 코드가 쿼리를 날리는 시점에 데이터를 즉시 가져오기
Fetch Join
- Join vs Fetch Join
- Join
- Fetch Join과 달리 연관 Entity에 Join을 걸어도 실제 쿼리에서 SELECT 하는 Entity는 오직 JPQL에서 조회하는 주체가 되는 Entity만 조회하여 영속화된다.
- 예를들어 팀이 있고 맴버가 있고, FetchType이 Lazy인 경우 일반 Join 으로 팀을 가져온다고 가정해보자.
- 이때 가져온 팀을 toString()하게되면 LazyInitializationException이 발생하게 된다.
- Team에 @ToString(exclude="members")를 설정하게 되면 members에 접근하지 않게 되고 LazyInitializationException 또한 발생하지 않게 됩니다.
- 혹은 팀에 있는 맴버의 값을 쓰게되면 영속성 컨텍스트에 member가 없기 때문에 추가적인 쿼리가 나간다. -> 이를 N + 1문제라고 한다.
- Fetch Join
- 조회의 주체가 되는 Entity 이외에 Fetch Join이 걸린 연관 Entity도 함께 SELECT 하여 모두 영속화된다.
- Fetch Join이 걸린 Entity 모두 영속화하기 때문에 FetchType이 Lazy인 Entity를 참조하더라도 이미 영속성 컨텍스트에 들어있기 때문에 따로 쿼리가 실행되지 않은 채로
N+1문제가 해결
된다. - 객체 그래프를 유지할때 사용하면 효과적이다.
- Join
무조건 Fetch Join 이득인가?
- 실무에서 Fetch Join 을 많이 사용하긴하지만 무조건 이득이 되는건 아니다.
- 예를들어 팀과 맴버가 일대다로 연관 되어있고 조건에 맞는 팀을 조회하려한다.-> 팀과 맴버를 조인하는 조건에는 데이터가 필요하나 -> 실제로 결과값으로 맴버의 어떠한 정보도 요구하지 않을 경우에는 일반 Join을 사용하는 것이 이득이다.
- 불필요한 엔티티를 Fetch Join을 사용해서 전부 영속성 컨텍스트에 올려서 쓰기는 것은 바람직하지 못하다.
- 필요한 Entity만 영속성 컨텍스트에 올려서 사용하는 것이 괜한 오작동을 미리 방지할 수 있는 방법 오히려 좋을 때가 있다.
Fetch Join 의 한계
- 페치 조인 대상에는 별칭을 줄 수 없다.
- 둘 이상의 컬렉션은 페치 조인할 수 없다. (N * M 으로 뻥튀기 될 수 있다.)
- 컬렉션을 페치 조인하면 페이징 API(setFirstResult, setMaxResults) 를 사용할 수 없다.
default_batch_fetch_size: 500
로 해결할 수 있다.- @OneToOne, @ManyToOne 에서는 페이징 가능하다.
REFERENCES
- 김영한님의 자바 ORM 표준 JPA 프로그래밍
- https://pearlluck.tistory.com/46
- https://cobbybb.tistory.com/18
'Database' 카테고리의 다른 글
쿼리연습 (겹치는 날짜 검사하기) (0) | 2023.04.07 |
---|---|
SQL (0) | 2022.05.30 |
프로시저 (0) | 2022.02.27 |
트랜잭션 격리수준(Isolation level) (0) | 2022.02.27 |
Spring Data Jpa Bulk Insert (0) | 2022.02.27 |