소개
게시글
질문&답변
2020.09.18
fetch join 관련 질문 드립니다!!
답변 감사합니다!! 쉽게 설명해주셔서 궁금했던 점들이 한번에 해결 되었습니다 :) 지금 체크해 보니까 querydsl 수업이 75% 수강한 상태인데 다음주 까지 빨리 완강하고 활용 2편도 수강 해봐야겠네요 다시한번 친절한 답변 감사드립니다!! :)
- 6
- 7
- 4.4K
질문&답변
2020.09.16
fetch join 관련 질문 드립니다!!
join에 관한 코드는 너무 길어서 이전처럼 깃으로 공유 드립니다. https://github.com/hjdeepsleep/toy/blob/master/src/test/java/com/github/hjdeepsleep/toy/domain/member/JoinTest2.java 1. fetch join과 일반 조인의 차이를 설명해주세요. 예제를 가지고, 실제 실행되는 JPQL, SQL, 그리고 애플리케이션에서 객체가 가지고 있는 데이터를 기반으로 비교해서 설명해주세요. 일반 join과 fetch join의 가장 큰 차이점은 엔티티를 조회 하는 시점입니다. 일반 join은 sql의 join과 동일 하게 동작 합니다. fetch join은 jpql을 이용하여 조회 할 때, 연관된 엔티티를 한번에 같이 조회하는 기능입니다. 별칭을 줄수 없기 때문에 select, where, 서브쿼리에는 사용이 불가 합니다. 둘 이상의 컬렉션을 fetch join 할 수 없습니다. 1-1) test3() 코드 작성죽 그동안 눈치 채지 못한 부분을 발견했습니다.. JoinTest2#before()에서 마지막에 em.flush(); em.clear();를 실행해 주었는데요 영속성 컨텍스트를 초기화 해주고 나니 test3()의 결과물이 제가 생각한데로 team.members에 age=10인 값만 들어갔습니다. 반면에 test4()에서는 쿼리 이전에 모든 team을 조회 하는 로직을 추가해 주었습니다. 그랬더니 test3()과 같은 쿼리에서도 서로 다른 결과가 확인 되었습니다. 1차 캐시에 캐싱되어서 그런거 같은데 맞나요? 1-2) test5()에서 projection을 이용해 team, member를 동시에 조회 하려고 해보았습니다. tuple를 출력해본 결과 team과 별도로 member가 아래와 같이 조회 되었습니다. [Team(id=1, name=team1, rank=1, members=[Member(id=5, username=member1-1, age=10), Member(id=6, username=member1-2, age=20), Member(id=7, username=member1-3, age=30)]), Member(id=5, username=member1-1, age=10)] DB조회 결과에도 age=10인 데이터만 조회 되었습니다만, team을 출력 하는 과정에서 select member 쿼리를 각각 실행 하는것을 확인 했습니다. team과 member를 함께 조회 하였는데도 team.members에 age=10이 아닌 모든 member가 포함되는 이유가 무엇인가요? 2. 제가 링크에 드린 글을 읽고 fetch join의 한계점을 정리해서 적어주세요. 그리고 한계점을 실제 테스트 코드로 작성해보세요. * fetch join 대상은 별칭을 줄수 없다. 이로 인해 select, where, 서브쿼리에서 fetch join 대상을 사용 할 수 없다. 하이버네이트는 별칭을 지원하지만 연관 데이터의 수에 대한 무결성이 깨질수도 있다. * 둘 이상의 컬렉션을 fetch 할 수 없다. 컬렉션x컬렉션의 곱이 만들어지므로 주의 해야 한다. * 페이징 API 사용이 불가하다. 컬렉션(OneToMany)에 fetch join을 사용하면 페이징 api가 사용 불가하다. 2-1) test3()에서 where조건에 fetch대상을 사용하였습니다. fetch 대상을 on, where 조건등에 사용하면 안된다고 하셨는데요. 왜그럴까 생각해봤는데요. 일단 test3()에서 나온 결과물이 아래와 같았습니다. Team(id=1, name=team1, rank=1, members=[Member(id=5, username=member1-1, age=10)]) Team(id=2, name=team2, rank=2, members=[Member(id=8, username=member2-1, age=10)]) 영한님의 책에는 연관된 데이터 수가 달라져서 조심해야 한다고 적혀 있는데요. 아래 2가지를 생각해 봤습니다. 첫번째. 위 결과에서 team1을 save 한다고 할 때 id=5(age=10)인 멤버만 저장이 되고 나머지 멤버들이 삭제 처리 돼서. 두번째. 1-1 질문에서 처럼(em.clear) 영속성 컨텍스트의 영향을 받게 되는데 그래서 test4()를 작성해 보았습니다. test4()에서는 test3()과 동일 작업을 하는데(query2), 쿼리 실행 이전에 모든 team을 조회 하는 로직을 추가해 보았습니다.(query1) 그랬더니 test3()과 같은 쿼리에서 서로 다른 결과값이 조회 되었습니다. 이 부분은 query1에서 조회한 결과가 캐싱 되었고, query2에서 조회 할 때 영향을 끼치는것 같습니다. 이렇게 같은 쿼리에서도 서로 다른 결과가 도출 될 수 있기 때문에 where조건에 사용하면 안되는것 같습니다. 맞을까요? 3. 원하는 결과를 얻기위해 DB SQL을 작성해주세요. 자바 코드 없이 데이터베이스 쿼리로만 해당 데이터를 조회하는 쿼리를 작성해주세요. select team.*, member.* from team left join member on(team.id = member.team_id and member.age >= 20) where team.rank 4. 원하는 결과를 얻기 위해 JPQL과 엔티티 대신에 DTO로 한번에 바로 조회해보세요. 우선 생성힌 dto의 소스 링크 공유 드립니다. https://github.com/hjdeepsleep/toy/blob/master/src/main/java/com/github/hjdeepsleep/toy/domain/mamber/dto/TeamDto.java https://github.com/hjdeepsleep/toy/blob/master/src/main/java/com/github/hjdeepsleep/toy/domain/mamber/dto/TeamDto2.java 4-1) test6()에서 TeamDto를 이용해 조회 해 보았습니다. 이전 테스트에서도 계속해서 team.members에는 모든 member가 조회 되고 있습니다. test6()에서 생선된 sql을 이용한 db조회 결과에서 age>=20인 memeber를 조회 했는데도 team.members에 조건에 맞지 않은 모든 멤버가 포함되고 있습니다. test7() 에서는 team당 2개씩 조회되는 member를 dto에 리스트로 받아보려 했지만 실패 했습니다. 영한님께서 강의중에 Tuple는 querydsl에 종속 되기 때문에 서비스 계층으로 return 하지 말라고 하셨는데요. 이 이유때문에 dto를 사용하는것이라고 생각 들었습니다.(@QueryProjection으로 인한 종속은 일단 제외) 제가 얻고 싶었던 결과 값은 Team 객체를 리턴 받고 각 team.members에는 나이가 20, 30인 member만 포함 되는 결과였는데요 test6()에서와 같이 member쪽에 아무리 조건을 주어도 team.members에는 결국 모든 memeber가 포함되는것을 확인 했습니다. 원하는 member를 얻기 위해선 team이 아니라 dto를 리턴 받고 해당 dto에서 원하는 결과 값을 도출 해야 하는것인가요?
- 6
- 7
- 4.4K
질문&답변
2020.09.15
fetch join 관련 질문 드립니다!!
안녕하세요 영한님 궁금증이 풀리지 않아 다시 한번 질문 드립니다 ㅠ 강의를 보면서 공부해본 내용을 이용해 위에서 질문한 select 쿼리를 실행해 보려고 여러가지 방법으로 시도해 보았으나 모두 실패 했습니다.. 우선 제가 테스트 해본 테스트 코드를 공유 드립니다.(너무 길어지는거 같아서 깃 주소로 공유 드립니다) https://github.com/hjdeepsleep/toy/blob/master/src/test/java/com/github/hjdeepsleep/toy/domain/member/JoinTest.java rank가 2등 이상인 팀과 함께, 해당 팀에 속한 20세 이상의 팀원을 조회 하려고 하였는데요 위의 테스트를 통해 결과적으로 저는 아래와 같은 결과를 Team엔티티를 통해 얻고자 하였습니다. { "name": "team1", "rank": 1, "members": [ { "name": "member1-2", "age": 20 }, { "name": "member1-3", "age": 30 } ] }, { "name": "team2", "rank": 2, "members": [ { "name": "member2-2", "age": 20 }, { "name": "member2-3", "age": 30 } ] }, { "name": "team4", "rank": 1, "members": [ ] }, 위에서 공유드린 깃의 7개의 테스트 모두 결과적으로 실패 하였습니다.. ㅠ Team 엔티티에 위 결과를 조회 하고 싶은데 어떤 방법으로 해결 해야 할까요? test5에서는 tuple로 조회해 [ Team(id=1, name=team1, rank=1, members=[ Member(id=5, username=member1-1, age=10), Member(id=6, username=member1-2, age=20), Member(id=7, username=member1-3, age=30)] ), Member(id=6, username=member1-2, age=20)] 이런 결과물을 얻었지만 나이가 30인 member1-3이 조회되지 않았습니다. test6에서는 test5에서의 문제를 해결해 보려고 했지만 member를 list로 받는데 실패 했습니다.. 이 문제를 어떻게 해결할 수 있나요??
- 6
- 7
- 4.4K