모르지 않다는 것은 아는것과 다르다.

Database

조인

채마스 2022. 2. 27. 02:00

조인의 종류

  • 먼저 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)

 

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문제가 해결된다.
      • 객체 그래프를 유지할때 사용하면 효과적이다.

 

무조건 Fetch Join 이득인가?

  • 실무에서 Fetch Join 을 많이 사용하긴하지만 무조건 이득이 되는건 아니다.
  • 예를들어 팀과 맴버가 일대다로 연관 되어있고 조건에 맞는 팀을 조회하려한다.-> 팀과 맴버를 조인하는 조건에는 데이터가 필요하나 -> 실제로 결과값으로 맴버의 어떠한 정보도 요구하지 않을 경우에는 일반 Join을 사용하는 것이 이득이다.
  • 불필요한 엔티티를 Fetch Join을 사용해서 전부 영속성 컨텍스트에 올려서 쓰기는 것은 바람직하지 못하다.
  • 필요한 Entity만 영속성 컨텍스트에 올려서 사용하는 것이 괜한 오작동을 미리 방지할 수 있는 방법 오히려 좋을 때가 있다.

 

Fetch Join 의 한계

  • 페치 조인 대상에는 별칭을 줄 수 없다.
  • 둘 이상의 컬렉션은 페치 조인할 수 없다. (N * M 으로 뻥튀기 될 수 있다.)
  • 컬렉션을 페치 조인하면 페이징 API(setFirstResult, setMaxResults) 를 사용할 수 없다.
    • default_batch_fetch_size: 500 로 해결할 수 있다.
    • @OneToOne, @ManyToOne 에서는 페이징 가능하다.

 

 

REFERENCES

'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