본문 바로가기

STUDY/JPA

[자바 ORM 표준 JPA 프로그래밍 - 기본편] 연관관계 매핑 기초

인프런 - 자바 ORM 표준 JPA 프로그래밍 (김영한님) 

 

1. 단방향 연관관계

객체의 참조와 테이블의 외래키를 매핑

 

  • Member(1)와 Team(M)은 일대다 관계
  • 테이블 연관관계
    • 테이블은 외래키로 조인을 사용해 연관된 테이블을 탐색
  • 객체 연관관계
    • 객체는 참조를 사용해서 연관된 객체를 탐색
@Entity
public class Member {
	@Id @GeneratedValue
    private Long id;
    
    @Column(name = "USERNAME")
    private String name;
    
    private int age;
    
    // 연관 관계 설정
    @ManyToOne
    @JoinColumn(name = "TEAM_ID")
    private Team team;
}
Team team = new Team();
team.setName("TeamA");
em.persist(team);


// Member 저장
Member member = new Member();
member.setName("member1");
member.setTeam(team);
em.persist(member);
  • @ManyToOne(fetch=FetchType.LAZY)
    • 지연 로딩 전략

 

 

 

2. 양방향 연관관계와 연관관계의 주인

* Member에서 Team 조회 시 

member.getTeam();

* Team에서 Member 조회 시 

  • DB에서는 조회가 가능하지만, 단방향 연관 관계에서는 조회 불가능 
  • Team.getMember()? 를 할 수 없으니까

 

  • DB에서 양방향 조회
    • 외래키 하나로 양쪽에서 조회 가능
    • Member에서 Team FK로 join해 Team을 알 수 있음
    • Team에서 Team FK로 join해 Member를 알 수 있음
  • 객체
    • 양방향 매핑이 필요

양방향 매핑 설정 

객체는 가급적 단방향 연관관계가 좋음 
// Member는 단방향과 동일
@Entity
public class Member {
	@Id @GeneratedValue
    private Long id;
    
    @Column(name = "USERNAME")
    private String name;
    
    private int age;
    
    // 연관 관계 설정
    @ManyToOne
    @JoinColumn(name = "TEAM_ID")
    private Team team;
}
@Entity
public class Team {
	@Id @GeneratedValue
    private Long id;
    
    private String name;
    
    // 양방향 연관관계 설정
    @OneToMany(mappedBy = "team")
    List<Member> members = new ArrayList<Member>();
}
// Team에서 Member 조회
Team findTeam = em.find(Team.class, team.getId());
List<Member> members = findTeam.getMembers();

 

 

객체와 테이블의 연관관계 차이

  • 객체의 연관관계 = 2개 
    • Member -> Team (단방향)
    • Team -> Member (단방향)
    • 객체를 양방향으로 참조하려면 둘 중 하나에서 외래키를 관리해야 함
  • 테이블 연관관계 = 1개
    • Member <-> Team (양방향, 방향이 없음)

 

연관관계의 주인(Owner)

  • 양방향 매핑 규칙
    • 객체의 두 관계 중 하나를 연관관계의 주인으로 지정
    • 연관관계의 주인만 외래키를 관리 (등록, 수정)
    • 주인이 아닌 쪽은 읽기만 가능 
      • mappedBy 속성으로 주인 지정
  • 연관관계의 주인은 어느쪽?
    • 외래키가 있는 곳을 주인으로! 
      • DB에서도 외래키가 있는 곳이 M, 없는 곳이 1
      • Member.team이 연관관계 주인 (@~ToOne)
    • 비즈니스적으로 중요해서 주인이 아니라 DB에 의해 결정, 즉 외래키가 있는 곳이 주인

 

양방향 연관관계와 연관관계의 주인 주의점 

  • 연관관계의 주인에 값을 입력하지 않음
  • mappedBy는 읽기 전용으로 JPA에서 insert, update시에 mappedBy 변수는 체크하지 않음
  • 아래 코드처럼 Member에는 Team을 저장하지 않고, Team에만 Member를 저장할 시 team.getMembers()는 읽기 전용이므로 DB에 저장되지 않아 Member테이블의 TEAM_ID에는 null로 저장됨
Team team = new Team();
team.setName("TeamA");
em.persist(team);

Member member = new Member();
member.setName("member1");

// 역방향만 연관관계 설정
team.getMembers().add(member);
em.persist(member);

 

  • 순수한 객체 관계를 고려하면 항상 양쪽 다 값을 입력해야 한다
  • team.getMembers().add(member)는 DB에 저장되지 않으므로 작성안해줘도 됨
  • 하지만 작성하지 않으면 1차 캐시에는 team.getMember()가 비어 있음
  • 즉, 양방향 연관관계면 양쪽에 값을 세팅하는 게 좋음
Team team = new Team();
team.setName("TeamA");
em.persist(team);

Member member = new Member();
member.setName("member1");

team.getMembers().add(member);
// 연관관계 주인에 값 설정 (필수)
members.setTeam(team);
em.persist(member);

 

  • 연관관계 편의 메소드 생성 
public void setTeam(Team team) {
	this.team = team;
    team.getMembers().add(this);
}

 

 

양방향 매핑 정리

  • 단방향 매핑만으로도 이미 연관 관계 매핑은 완료
  • 양방향 매핑은 반대 방향으로 조회(객체 그래프 탐색) 기능이 추가된 것 뿐
  • JPQL에서 역방향으로 탐색할 일이 많음