BE/JPA

[JPA] 10. 연관관계 매핑

멍목 2022. 4. 17. 14:10
반응형

자바 ORM 표준 JPA 프로그래밍

 

 

 

 

 

이 포스팅에서 작성하는 내용은 자바 ORM 표준 JPA 프로그래밍 (김영한 지음) 에서 발췌하였습니다.

 

 

 

1. 연관관계가 필요한 이유

'객체지향 설계의 목표는 자율적인 객체들의 협력 공동체를 만드는 것' - 조영호(객체지향의 사실과 오해)

 

- 객체 vs 테이블

  • 테이블 : 외래 키를 이용해 조인함.
  • 객체 : 참조를 이용함

 

2. 관계형 DB를 기준으로 엔티티를 설계한 경우

1) Member 객체와 Team 객체(Member는 하나의 팀에만 속할 수 있음)

@Entity
@Table(name="TEAM_TWO")
public class Team {
    @Id @GeneratedValue
    @Column(name = "TEAM_ID")
    private Long id;
    
    private String name;
    
    ...
}
@Entity
@Table(name="MEMBER_TWO")
public class Member {
    @Id @GeneratedValue
    @Column(name="MEMBER_ID")
    private Long id;
    
    @Column(name = "USERNAME")
    private String name;
    
    // 외래키를 넣어서 사용하는 방식. 객체지향스럽지 않고 관계형 DB에 초점이 맞춰진 설계
    @Column(name = "TEAM_ID")
    private Long teamId;
    
    ...
 }

 

2) Insert 예시

try{
    Team team = new Team();
    team.setName("TEAM_A");
    em.persist(team);

    Member member = new Member();
    member.setName("홍길동");
    // Team의 외래키를 직접 넣어주는 방식 (객체지향스럽지 못함)
    //member.setTeamId(team.getId());

    em.persist(member);
    
    tx.commit();
} catch(Exception e){
	...
}
...

 

3) 단방향 매핑

아래의 예시에서는 @ManyToOne 어노테이션과 @JoinColumn을 이용

 

1) Member 객체와 Team 객체(Member는 하나의 팀에만 속할 수 있음)

@Entity
@Table(name="TEAM_TWO")
public class Team {
    @Id @GeneratedValue
    @Column(name = "TEAM_ID")
    private Long id;
    
    private String name;
    
    ...
}
@Entity
@Table(name="MEMBER_TWO")
public class Member {
    @Id @GeneratedValue
    @Column(name="MEMBER_ID")
    private Long id;
    
    @Column(name = "USERNAME")
    private String name;

    @ManyToOne
    @JoinColumn(name = "TEAM_ID")
    private Team team;
    
    ...
}

 

2) Insert 예시

try{
    Team team = new Team();
    team.setName("TEAM_A");
    em.persist(team);

    Member member = new Member();
    member.setName("홍길동");
    
    // 연관관계 어노테이션을 이용해서 간편하게 사용 가능
    member.setTeam(team);

    em.persist(member);
    
    tx.commit();
} catch(Exception e){
	...
}
...

 

4) 양방향 매핑

Team객체에서도 Member객체를 호출 할 수 있도록 설정 (따지고 보면 단방향을 Team에다가도 추가한 꼴임)

아래의 예시에서는 @OneToMany 어노테이션과 mappedBy 속성을 이용

 

1) Member 객체와 Team 객체(Member는 하나의 팀에만 속할 수 있음)

@Entity
@Table(name="TEAM_TWO")
public class Team {
    @Id @GeneratedValue
    @Column(name = "TEAM_ID")
    private Long id;
    private String name;

    // Team에는 많은 Member가 있기 때문에 OneToMany
    // mappedBy로 매핑
    @OneToMany(mappedBy = "team")
    private List<Member> members = new ArrayList<>();
    
    ...
}
@Entity
@Table(name="MEMBER_TWO")
public class Member {
    @Id @GeneratedValue
    @Column(name="MEMBER_ID")
    private Long id;
    
    @Column(name = "USERNAME")
    private String name;
    
    // 외래키를 넣어서 사용하는 방식. 객체지향스럽지 않고 관계형 DB에 초점이 맞춰진 설계
    /*
    @Column(name = "TEAM_ID")
    private Long teamId;
    */
    @ManyToOne
    @JoinColumn(name = "TEAM_ID")
    private Team team;

	...
}

 

2) Insert 이후, Team에 속한 Member 객체를 가져오기

* em.flush()와 em.clear()로 쿼리를 빨리 보내고 캐시를 지워서 쿼리에서 가져올 수 있도록 함

try{
    Team team = new Team();
    team.setName("TEAM_A");
    em.persist(team);

    Member member = new Member();
    member.setName("홍길동");
    
    // 연관관계 어노테이션을 이용해서 간편하게 사용 가능
    member.setTeam(team);

    // flush와 clear를 통해 미리 쿼리를 날리고 영속성 컨텍스트를 비움. (추후에 1차캐시에서 가져오는 것이 아닌 쿼리에서 가져오게 하기 위함)
    em.flush();
    em.clear();

    Member findMember = em.find(Member.class, member.getId());
    List<Member> members = findMember.getTeam().getMembers();;
    for(Member m : members){
        System.out.println("member_name : " + m.getName());
    }
    
    tx.commit();
} catch(Exception e){
	...
}
...

 

 

5) 연관관계의 주인과 mappedBy

- 연관관계의 주인

  • 양방향 매핑 시 사용
  • 객체의 두 관계중 하나를 연관관계의 주인으로 설정
  • 연관관계의 주인만이 외래 키를 관리함(등록 / 수정)
  • 주인이 아닌 쪽은 읽기만 가능
  • 주인은 mappedBy 속성을 사용하지 않음
  • 주인이 아니면 mappedBy 속성으로 주인이 누구인 지 지정
  • 외래 키가 있는 객체를 주인으로 정하면 보다 간편함

 

6) 양방향 매핑 시, 자주 하는 실수 모음

  • 연관관계의 주인인 객체는 값을 반드시 넣어줘야함.
    • 하지만, 순수한 객체 관계를 고려한다면 양쪽에 다 넣어주는 것이 좋음.
  • toString(), lombok, JSON 관련하여 생성할 때 무한 루프를 조심하자.

 

7) 양방향 매핑 정리

  • 설계 시, 양방향 매핑은 추후에 하고, 단방향 매핑만으로 연관관계를 매핑하는 것이 좋다.
  • 양방향 매핑 : 반대 방향으로 조회 기능이 추가된 것이라고만 보는 것이 좋다.
  • JPQL에서 역방향으로 탐색할 일이 많음
  • 단방향 매핑을 잘 하고 양방향은 필요할 때 추가해도 됨(테이블에 영향 X)
반응형