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

gusdn85554님의 프로필 이미지
gusdn85554

작성한 질문수

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

양방향 연관관계와 연관관계의 주인 2 - 주의점, 정리

연관관계에 대한 정의

작성

·

212

0

강사님 안녕하십니까 강의 너무 재밌게 잘 듣고 있습니다!

다름이 아니라 이번 강의에서 다룬 Team에 존재하는 Member 객체 리스트는 왜 조회되지 않을까?에 대한 부분이 헷갈려서 해당 강의 부분에 있는 QnA를 보면서 코드도 작성해보면서 공부했는데, 제가 생각한 것이 맞는지 궁금하여 질문드립니다.

 

1) 1번만 주석처리 안할 경우

Team에 있는 Member형 리스트에서 member가 조회됨. 쿼리는 commit 때 나감

왜?

여기서 select쿼리가 나가지 않는 이유는 캐시에 존재하기 때문

2) 2번만 주석처리 안할 경우

insert 쿼리 나가고, DB에 select 쿼리 보내어 조회

왜?

clear해서 영속성 컨텍스트는 비워져있어, db에서 가져오면서 캐시에 저장함

3) 1, 2번 둘 다 주석처리할 경우

findTeam.getMembers() 하면 Member형 리스트는 불러올 수 있다.

왜?

Team 안에서 리스트를 이미 초기화해주었기 때문이다.

그러나 이 안에 아무것도 들어있지 않아 member를 조회할 수 없음

 

4)

여기서 2번이 중요

어떻게 Team의 members에 객체를 add하지 않았는데 조회가 될까?

영속성 컨텍스트에 team이 없음(clear 때문)

그래서 JPA가 DB에서 까지 다녀와 Team 객체를 만든다.

이 때, 연관관계 매핑이 되어있기 때문에 team.getMembers(), member.getTeam()의 값을 채워준다.

 

https://www.inflearn.com/questions/27517

위 qna에 관해 강사님께서

새로운 영속성 컨텍스트는 member가 없기 때문에 DB에서 새로 member를 조회하고, JPA가 member 객체를 생성합니다.

그리고! JPA가 member 객체를 생성할 때, 연관관계 매핑이 되어 있기 때문에  member.getTeam()은 물론이고, team.getMembers() 모두 값을 채워줍니다.(물론 지연 로딩을 사용할 수도 있습니다.)

방금 말씀드린 부분을 코드로 짜서 하나씩 실행을 해보면 이해가 되실꺼에요^^!

이렇게 말씀을 해주셨습니다.

5) JPA가 채워준다는 의미는 그냥 아.. db에서 가져오면 연관관계가 된 프로퍼티를 채워준다? 라고 생각하면 되나요? Member에서는 Team 프로퍼티를 채우고, Team에서는 Member 프로퍼티를 채운다. 요런 느낌으로요!

 

6) 5번과 관련해서 for문에서 members를 조회하는데 왜 select 쿼리를 보내는지 궁금합니다. db에서 가져오면 채워준다고 말씀을 하셨는데 다른 걸 채워주는건가요??

 

try {

            Team team = new Team();
            team.setName("TeamA");
            em.persist(team);

            Member member = new Member();
            member.setUsername("member1");
            em.persist(member);

            
//            team.getMembers().add(member); //1번

            em.flush(); //2번
            em.clear();

            System.out.println("====================");

            Team findTeam = em.find(Team.class, team.getId());
            List<Member> members = findTeam.getMembers();

            System.out.println("====================");
            Member member2 = em.find(Member.class, member.getId());
            System.out.println("====================");

            System.out.println("member2  = " + member2.getId());
            System.out.println("======================");
            for (Member member1 : members) {
                /////1번 질문
                System.out.println("member1 = " + member1.getUsername());
            }
            System.out.println("======================");

            tx.commit();

 

긴 글 죄송합니다..

처음에는 이해가 됐는데 코드를 작성할수록 이해가 되질 않아서 질문드립니다.

감사합니다!

 

답변 4

0

gusdn85554님의 프로필 이미지
gusdn85554
질문자

1,2,3,4번은 제가 이해한 내용이 맞는지 확인하고 싶어서 써봤습니다!!

0

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

안녕하세요. gusdn85554님

마지막 부분이 궁금하신 내용이었군요.

Q: JPA가 채워준다는 의미는 jpa가 @OneToMany(mappedBy)와 @ManyToOne, @JoinColumn을 보고 알아서 채워준다는 말씀이신가요??(만약 그렇다면 너무 신기해서 그렇습니다)

-> 네 맞습니다. JPA가 채워줍니다. 추가로 이후에 프록시 부분까지 학습해보시면 더 깊이있게 이해하실 수 있을거에요.

감사합니다.

0

gusdn85554님의 프로필 이미지
gusdn85554
질문자

1) ( em.flush(), em.clear()를 하지 않을 경우 + 연관관계의 주인에 값을 입력하지 않을 경우 ) ->  member에 teamId  매핑 안됨 + members 조회 됨

1번에서 select 쿼리가 나가지 않는 이유는 1차 캐시에 저장된 값을 가져오기 때문이다.

2번은 연관관계의 주인에 값을 입력하지 않아도 member를 조회할 수 있는 이유는 1번과 같이 1차 캐시에서 값을 가져오기 때문이다.

 

```java

Team team = new Team();
team.setName("TeamA");
em.persist(team);

Member member = new Member();
member.setUsername("member1");
team.getMembers().add(member);

em.persist(member);

System.out.println("====================");
System.out.println("1");

Team findTeam = em.find(Team.class, team.getId());
List<Member> members = findTeam.getMembers();

System.out.println("======================");
System.out.println("2");

for (Member member1 : members) {
System.
out.println("member1 = " + member1.getUsername());
}
System.
out.println("======================");

tx.commit()
;


//결과값
==================== 1번 ====================== 2번 member1 = member1 ======================

```

 

2) ( em.flush(), em.clear()를 하지 않을 경우 + 연관관계의 주인에 값을 입력할 경우 ) --> member에 teamId 매핑됨 + members 조회 안됨

member1를 조회해도 아무것도 안나온다. team의 member객체에 아무것도 넣어주지 않았기 때문.

```java

Team team = new Team();
team.setName("TeamA");
em.persist(team);

Member member = new Member();
member.setUsername("member1");
member.setTeam(team);

em.persist(member);

System.out.println("====================");
System.out.println("1");

Team findTeam = em.find(Team.class, team.getId());
List<Member> members = findTeam.getMembers();

System.out.println("======================");
System.out.println("2");

for (Member member1 : members) {
System.
out.println("member1 = " + member1.getUsername());
}
System.
out.println("======================");

tx.commit()
;

//결과값
==================== 1번 ====================== 2번 ======================

```

 

3) ( em.flush(), em.clear()를 할 경우 + 연관관계의 주인에 값을 입력하지 않을 경우) --> 매핑 안됨 + members 조회 안됨

이 때는 members에서도 조회가 되지 않는다. 왜냐하면 강의에서 연관관계의 주인에 값을 입력하지 않을 경우에 "읽기 전용"으로 된다.

왜 읽기 전용으로 될까?

team을 영속성 컨텍스트에 저장을 하고 team.getMembers().add(member)를 하면 그냥 메모리? 상에서만 저장이 되고 member를 영속화 컨텍스트에 저장하고 flush를 할 때, team.getMembers().add(member) 이 부분은 반영되지 않는다.

 

```java

Team team = new Team();
team.setName("TeamA");
em.persist(team);

Member member = new Member();
member.setUsername("member1");
team.getMembers().add(member);

em.persist(member);

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

System.out.println("====================");
System.out.println("1");

Team findTeam = em.find(Team.class, team.getId());
List<Member> members = findTeam.getMembers();

System.out.println("======================");
System.out.println("2");

for (Member member1 : members) {
System.
out.println("member1 = " + member1.getUsername());
}
System.
out.println("======================");

tx.commit()
;

//결과 값
==================== 1번 Hibernate: select team0_.TEAM_ID as TEAM_ID1_1_0_, team0_.name as name2_1_0_ from Team team0_ where team0_.TEAM_ID=? ====================== 2번 Hibernate: select members0_.TEAM_ID as TEAM_ID3_0_0_, members0_.MEMBER_ID as MEMBER_I1_0_0_, members0_.MEMBER_ID as MEMBER_I1_0_1_, members0_.TEAM_ID as TEAM_ID3_0_1_, members0_.USERNAME as USERNAME2_0_1_ from Member members0_ where members0_.TEAM_ID=? ======================

```

 

4) ( em.flush(), em.clear()를 할 경우 + 연관관계의 주인에 값을 입력할 경우) --> 매핑 됨 + members 조회됨

https://www.inflearn.com/questions/27517

위 qna에 관해 강사님께서

새로운 영속성 컨텍스트는 member가 없기 때문에 DB에서 새로 member를 조회하고, JPA가 member 객체를 생성합니다.

그리고! JPA가 member 객체를 생성할 때, 연관관계 매핑이 되어 있기 때문에  member.getTeam()은 물론이고, team.getMembers() 모두 값을 채워줍니다.(물론 지연 로딩을 사용할 수도 있습니다.)

방금 말씀드린 부분을 코드로 짜서 하나씩 실행을 해보면 이해가 되실꺼에요^^!

이렇게 말씀을 해주셨습니다.

JPA가 채워준다는 의미는 jpa가 @OneToMany(mappedBy)와 @ManyToOne, @JoinColumn을 보고 알아서 채워준다는 말씀이신가요??(만약 그렇다면 너무 신기해서 그렇습니다)

```java

Team team = new Team();
team.setName("TeamA");
em.persist(team);

Member member = new Member();
member.setUsername("member1");
member.setTeam(team);

em.persist(member);

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

System.out.println("====================");
System.out.println("1");

Team findTeam = em.find(Team.class, team.getId());
List<Member> members = findTeam.getMembers();

System.out.println("======================");
System.out.println("2");

for (Member member1 : members) {

System.out.println("member1 = " + member1.getUsername());
}

System.out.println("======================");

tx.commit();

```

(연관관계 편의 메서드는 넣지 않았습니다)

 

해당 관련된 내용의 질문이 많은 것 같아서 모든 경우의 수(?)를 생각해보고 적어봤습니다..

제 생각을 한 번 정리해보면서, 추후에 다른 분들이 궁금하실 때 이 글을 읽고 이해가 잘 되셨으면 하는 바램에 적어봤습니다!

 

감사합니다.

 

0

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

안녕하세요. gusdn85554님

도움을 드리고 싶은데, 질문이 명확하게 잘 이해가 되지 않습니다.

질문이 여러가지고 섞여 있어서 머리속에 정리가 잘 되지 않습니다.

각각의 케이스별로 완전히 분리해서 설명해주시면 좋을 것 같아요.

각각의 케이스 별로 코드를 별도로 분리해서 만들고, 실행 결과도 각각 따로 다 남겨서 다시 질문을 남겨주시겠어요?

gusdn85554님의 프로필 이미지
gusdn85554
질문자

강사님 말씀 이해됐습니다..

저렇게 질문을 한 저도 이해가 안되네요..

gusdn85554님의 프로필 이미지
gusdn85554

작성한 질문수

질문하기