Development/Spring

[JPA] JPA Join 이슈, FETCH JOIN의 사용

hyelie 2022. 10. 4. 15:00

Join 방향 이슈

A : B = 1 : N의 관계일 때 일반적으로 B에서 ManyToOne을 가지고 있다. 이 경우 B Join FETCH A는 되지만 A Join FETCH B를 하면 작동하지 않는다. 왜냐하면 B에서 A 정보는 있지만 A에서 B 정보는 없기 때문이다. 이를 해결하기 위해서는

 

- 양방향 관계를 넣던가,

- B.getA()로 A 정보를 가져오던가,

- SQL JOIN하듯

SELECT b FROM A a JOIN B b ON b.A = a;

이렇게 하면 된다. 그냥 INNER JOIN하는 것 처럼 된다. 다만 이러면 join을 해버리는 것이기 때문에 성능상 이슈를 생각해야 한다.

 

 

 

Fetch Join의 사용 이유

그리고 또 하나. FETCH JOIN을 사용하는 이유를 깨달은 것 같은데.

 

처음에는 B에 있는 정보를 찾기 위해서

SELECT b FROM B b WHERE B.a = ?;

이런 식으로 B에 있는 A의 값이 얼마인 것에 대해 검색했다. 그런데 오늘 짜다가 B.a가 NULL인 것을 발견했다. 아 이렇게 하면 안되는구나. 이유는, JPA 영속성에 DB의 모든 값이 있는 게 아니라 필요한 걸 필요할 때 불러오는 형식인 FetchType.LAZY를 사용했는데 (N+1 문제를 막기 위해) DB에만 값이 있고, 영속성에는 값이 없을 경우 JPA가 B 내에 있는 a를 불러올 생각을 안한다. FetchType을 LAZY를 사용했기 때문에 이게 맞는 동작 방식이고, 따라서 null값이 된다. 

 

이걸 해결하기 위해 FetchType.EAGER로 바꿔버리면 B.a를 조회할 때마다 매번 query가 날아간다. N+1 문제인 것이다. 그래서 조회를 할 때 FETCH JOIN을 해서 B와 연관되어 있는 모든 A를 불러와서 N+1 문제를 막음과 동시에 필요할 때 fetch join을 사용해서 필요한 값을 한 번에 가져오는 것이다.

 

SELECT b FROM B b JOIN FETCH b.a WHERE b.a = ?;

이런식으로 짜면 양방향 구성을 하지 않고 join 1번으로 b.a에 해당하는 모든 b, a를 영속성으로 불러올 수 있게 되는 것 같다.

 

처음에는 "아니 JOIN 할 이유가 없는데 왜 JOIN하는 거지"라고 생각했었는데, 너무 DB에 초점이 맞춰진 생각이었다는 것을 느꼈고, Fetch Join과 join의 차이를 너무 몰랐구나 싶다. DB에는 있는 값이니까 당연히 검사할 수 있을 줄 알았는데 아니었다. 영속성에 데이터가 있는지 없는지 모르기 때문에 JOIN FETCH를 하는 것을 알게 되었다. 이제 조금 감이 많이 온다.