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를 하는 것을 알게 되었다. 이제 조금 감이 많이 온다.
'Development > Spring' 카테고리의 다른 글
[Spring + Jwt] Spring Boot + Spring Security + Redis + Jwt를 이용한 회원가입 및 로그인 (0) | 2022.10.05 |
---|---|
[Spring + Redis] Spring Redis 연동 (0) | 2022.10.04 |
[JPA] JPQL FROM절 subquery를 해결하는 방법 (inline view) (0) | 2022.10.04 |
[Spring] DTO와 Entity 간의 변환 (0) | 2022.10.04 |
[JPA] JPA @IdClass에 관해,,, (0) | 2022.10.04 |