복습을 위한

양방향 연관관계 주의사항 본문

JPA

양방향 연관관계 주의사항

ho042479 2024. 3. 2. 19:44

아래 두 개의 엔티티가 있다. 

@Entity
public class Team {

    @Id@GeneratedValue
    @Column(name = "TEAM_ID")
    private Long id;
    private String name;

    @OneToMany(mappedBy = "team")
    private List<Member> members = new ArrayList<>();

 

@Entity
public class Member {
    @Id
    @GeneratedValue
    @Column(name = "MEMBER_ID")
    private Long id;

    @Column(name = "USERNAME")
    private String username;

//    @Column(name = "TEAM_ID")   <--(참조 대신에 외래 키를 그대로 사용) 객체지향적이지않다
//    private Long teamId;

    @ManyToOne
    @JoinColumn(name = "TEAM_ID")                //여러 멤버가 하나의 팀에 속함
    private Team team;

 

 

값을 조회하는 코드를 살펴보자

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();
 
Team findTeam = em.find(Team.class, team.getId());
List<Member> members = findTeam.getMembers();
 
for (Member m : members) {
    System.out.println("m = " + m.getUsername());
}
 
tx.commit();

 

위와 같이 Team엔티티와 Member엔티티에 값을 넣는 상황이다. 

Team은 List<Members>를 필드로 갖지만 위코드에선 아직 값을 넣어주지않았다. 

그리고 멤버 엔티티에는 Team을 넣어주었다. 

 

persist로 두 객체를 영속성 컨테이너로 올린다. (1차캐시상태)

근데 여기서 flush clear를 한다면 영속성 컨텍스트를 새로 만들거나 완전히 초기화된 다음에 team을 조회하므로, 이것은 우리가 만든 team 객체 인스턴스가 아니라 JPA가 만들어서 제공하는 team 객체를 받을 수 있다. 이 경우 JPA가 team.members를 조회하면 읽기 용으로 추가로 데이터베이스에서 값을 읽어서 데이터 조회를 제공한다

우린 양방향 연관관계에서 주인인 Member엔티티에만 값을 넣었는데 team에도 members가 조회된다는 말이다. 

데이터베이스에 반영시 jpa는 연관관계의 주인만본다는 것이다. MappedBy로 된 가짜매핑 비주인은 jpa가 안본다. 읽기전용이다. 그러니 주인에서 값을 변경해줘야한다.

 

 

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();
 
Team findTeam = em.find(Team.class, team.getId());
List<Member> members = findTeam.getMembers();
 
for (Member m : members) {
    System.out.println("m = " + m.getUsername());
}
 
tx.commit();

하지만

만약 위코드에서 flush와 clear 코드를 빼본다고 치자.

그럼 데이터베이스에 반영이 되지않은 상태이다. persist를 통해 영속성 컨테이너 위에만 올라가있는 상태이다. 여기서 중요한 건 그 영속성컨테이너의 Member객체와  Team객체는 순수 자바객체라는 것이다. 우리가 만든 team 객체 인스턴스라는 뜻이다. Team객체에 우리가 members를 세팅안해줬고 조회시 데이터베이스에서 값을 읽어오는 것이 아니라 영속성 컨테이너의 순수자바객체에서 읽어오는 것이기 때문에 이 상황에서는 값을 읽어오지못한다.  

 

 

 

결론은 

1. 순수 객체 상태를 고려해서 항상 양쪽에 값을 설정하자   (jpa사용안하는 테스트케이스 작성 시에서도 문제가 있기 때문이다.) 

2. 연관관계 편의 메소드를 생성하자

 

 

 

 

참고

https://www.inflearn.com/course/ORM-JPA-Basic/dashboard