JPA JOIN 시리즈 (2/5)

이전 편: [JPA] 1. JOIN 유형 개요

다음 편: [JPA] 3. LEFT OUTER JOIN

INNER JOIN은 가장 기본적인 JOIN이다. 두 테이블을 연결할 때 양쪽 모두에 매칭되는 행만 결과에 포함시킨다. 조건을 만족하지 않는 행은 어느 쪽이든 결과에서 제외된다.

시리즈 안내

- JOIN 유형 개요

- INNER JOIN ← 현재 문서

- LEFT OUTER JOIN

- RIGHT OUTER JOIN

- JPA fetch join

동작 원리

feed 테이블과 user 테이블을 INNER JOIN하는 상황이다.

SELECT f.*, u.*
FROM feed f
INNER JOIN user u ON f.author_id = u.id

feed.author_iduser.id가 일치하는 행만 결과에 포함된다.

다이어그램 추가 예정

여기에 INNER JOIN의 집합 교집합 관계를 보여주는 다이어그램 필요.

만약 특정 Feed의 author_id가 NULL이거나, 대응하는 User가 존재하지 않으면 그 Feed는 결과에서 빠진다.

언제 쓰는가

연관된 데이터가 반드시 존재하는 경우에 쓴다. "작성자가 없는 피드는 없다"는 전제가 성립할 때 INNER JOIN이 적합하다.

반대로, 연관된 데이터가 없는 경우도 결과에 포함시켜야 한다면 LEFT OUTER JOIN을 써야 한다. 예를 들어 "아직 피드를 작성하지 않은 유저도 목록에 포함"이라면 INNER JOIN으로는 불가능하다.

JPA JPQL에서의 INNER JOIN

JPQL에서는 JOIN을 쓰면 기본이 INNER JOIN이다.

@Query("SELECT f FROM Feed f JOIN f.author a WHERE a.name = :name")
List<Feed> findByAuthorName(@Param("name") String name);

JOIN f.author는 SQL의 INNER JOIN user ON feed.author_id = user.id로 변환된다. 작성자가 없는 Feed는 결과에서 제외된다.

이때 주의할 점은, 위 쿼리는 조건 필터링 목적이다. author 엔티티를 조건에 사용했지만 결과에 로딩되지는 않는다. feed.getAuthor()를 호출하면 LAZY 로딩이 별도로 일어난다.

연관 엔티티를 함께 로딩하고 싶다면 JOIN FETCH를 써야 한다. 자세한 내용은 05 JPA fetch join 참고.

자주 하는 실수

JOIN했는데 연관 엔티티가 로딩되지 않는다고 착각

JPQL에서 JOIN f.author를 썼다고 author가 메모리에 올라오는 게 아니다. 일반 JOIN은 SQL JOIN처럼 행을 필터링하는 역할만 한다. author를 함께 가져오려면 반드시 JOIN FETCH를 써야 한다.

[!BUG] NULL 가능성 있는 FK에 INNER JOIN 사용

author_id가 NULL일 수 있는 구조라면 INNER JOIN을 쓰면 해당 행이 결과에서 조용히 빠진다. 에러 없이 데이터가 누락되는 버그가 된다. 이 경우 LEFT OUTER JOIN을 사용해야 한다.