인프런 커뮤니티 질문&답변

sbl133님의 프로필 이미지

작성한 질문수

자바 ORM 표준 JPA 프로그래밍 - 기본편

연관관계매핑과 영속성컨텍스트에 관한 질문입니다.

22.02.17 00:50 작성

·

132

0

안녕하세요 영한님. 영한님의 jpa강의들 정말 잘 듣고 있습니다.

다름이 아니라 연관관계매핑과 영속성컨텍스트에 관해 이해가 정확히 되지 않는점이 생겨서 이렇게 질문드리게 됐습니다.

제가 맨 처음 궁금했던 내용은 다음과 같습니다.

Member와 Team이 다대일 양방향 연관관계를 맺고있는 상황에서 모든 member들과 team들을 조회한 후에 member.getTeam()을 한다면, 해당 객체들을 영속성 컨텍스트가 관리 중 이므로 LazyLoading으로 인한 별도의 쿼리없이 member와 관련된 team의 정보를 조회할 수 있을거다..

그래서 아래와 같은 코드를 작성하여 실행해 보았습니다.

            Team team1 = new Team();
            team1.setName("팀1");
            em.persist(team1);

            Member member1 = new Member();
            member1.setUsername("관리자1");
            member1.setTeam(team1);
            em.persist(member1);

            Member member2 = new Member();
            member2.setUsername("관리자2");
            member2.setTeam(team1);
            em.persist(member2);

            Team team2 = new Team();
            team2.setName("팀2");
            em.persist(team2);

            Member member3 = new Member();
            member3.setUsername("관리자3");
            member3.setTeam(team2);
            em.persist(member3);

            Member member4 = new Member();
            member4.setUsername("관리자4");
            member4.setTeam(team2);
            em.persist(member4);

            em.flush();
            em.clear();

            List<Member> members = em.createQuery("select m from Member m", Member.class)
                    .getResultList();
            List<Team> teams = em.createQuery("select t from Team t", Team.class)
                    .getResultList();

            for (Member member : members) {
                System.out.println("member = " + member);
                System.out.println(member.getTeam());
                System.out.println(member.getTeam().getName());
            }

위 코드의 실행결과는 다음과 같았습니다.

제 예상대로 별도의 쿼리 없이 처음 Member와 Team 전체를 조회하는 두번의 쿼리로 member와 member에 연관된 team까지 조회가능했습니다.

좀 더 확실히 하기 위해서 반대로 team.getmembers()를 사용하는 다음과 같은 코드를 실행해 보았습니다.

            . . .

            em.flush();
            em.clear();

            List<Member> members = em.createQuery("select m from Member m", Member.class)
                    .getResultList();
            List<Team> teams = em.createQuery("select t from Team t", Team.class)
                    .getResultList();

            for (Team team : teams) {
                System.out.println("team = " + team);
                System.out.println(team.getMembers());
                System.out.println(team.getMembers().get(0).getUsername());
            }

 

그 결과는 다음과 같았습니다.

위 결과를 통해 team.getMembers()를 사용할때는 모든 member들을 영속성 컨텍스트가 관리중임에도 불구하고 LazyLoading으로 인한 별도의 쿼리가 나가는것을 알 수 있었습니다.

그래서 저는 다음과 같은 추측을 하게 되었습니다.

1. MEMBER테이블에는 외래키가 있기 때문에 member에 대한 데이터를 받아 jpa에서 member객체를 생성할때 member.team에 대한 프록시객체에 외래키값을 넣을 수 있겠다. 그 후에 member.team의 실제 정보가 필요할때 영속성 컨텍스트를 참조해 실제 team객체를 찾아올 수 있을 것이다. 하지만 team.members의 경우에는 TEAM테이블에 외래키를 가지고 있지 않기 때문에 별도의 쿼리가 필요한 것 아닐까?

2. 1번이 아니라면 혹시 영속성 컨텍스트에 있는 데이터들이 완전하지 않을 수 있기 때문에(team.members가 데이터 일관성을 해칠 가능성이 있으므로)jpa측에서 컬렉션 형태의 연관관계 프록시가 영속성 컨텍스트를 참조하지 못하도록 막아놓은것이 아닐까?

먼저 1번 추측을 확인하기 위해 Member(연관관계주인)와 Locker 일대일 양방향 관계에서도 비슷한 코드를 작성하여 실행해 보았습니다.

그 결과 member.locker와 locker.member 두 경우 모두 별도의 쿼리 없이 각각의 전체조회쿼리 한번씩 총 두번의 쿼리로 해당 객체에 연관된 객체정보를 조회 할 수 있었습니다. 따라서 테이블에 외래키가 있든 없든 상관없이 프록시는 영속성 컨텍스트를 참조한다는것을 알 수 있었습니다.

 

따라서 제 생각에는 2번을 이유로 영속성 컨텍스트에 모든 Member에 관한 데이터가 있음에도 불구하고 team.getMembers()를 사용할때 별도의 쿼리가 나가는것 같은데 확실히 맞는것인지 궁금합니다.

또한 프록시가 영속성 컨텍스트를 참조해서 진짜 객체를 찾으려면 프록시도 pk값을 가지고 있어야 될거 같은데, 일대일 양방향 연관관계에서 연관관계의 주인이 아닌 Locker에 경우 테이블에 외래키가 존재하지 않아 locker.member프록시에 pk값을 넣기 힘들것 같은데 어떤 방식으로 영속성컨텍스트를 참조하는지 궁금합니다. 

 

질문이 너무 길고 장황해서 죄송하다는 말씀드리고 싶습니다..ㅠㅠ 

긴 글 읽어주셔서 감사드리고 영한님의 좋은 강의들에 항상 감사하고 있습니다!!

답변 1

0

김영한님의 프로필 이미지
김영한
지식공유자

2022. 02. 21. 22:55

안녕하세요. sbl133님

team.getMembers()는 모든 Member를 다 조회해야 합니다. 해당 Member가 영속성 컨텍스트에 일부 있을수도 있지만 일부는 DB에 있을 수도 있지요. 그래서 모든 Member를 조회하는 쿼리가 실행됩니다.

감사합니다.

 

sbl133님의 프로필 이미지

작성한 질문수

질문하기