JPA 연관관계 매핑

2021-02-02

JPA를 공부하면서 정리를 한 내용들 입니다.

-참고 김영한 개발자님 : [토크ON세미나] JPA 프로그래밍 기본기 다지기 - 연관관계 매핑 T아카데미

4강. JPA 연관관계 매핑

1.객체를 테이블에 맞추어 모델링

(외래키 식별자를 직접 다룸)

@Entity
public class Member {
  
  @Id @GeneratedValue
  private Long id;
  
  @Column
  private String name;
  private int age;
  
  @Column(name = "TEAM_ID")
  private Long teamID;
  ...
}

@Entity
public class Team {
  
  @Id @GeneratedValue
  private Long id;
  private String name;
  ...
}
// 팀 저장
Team team = new Team();
team.setName("TeamA");
em.persist(team);

// 회원 저장
Member member = new Member();
member.setName("member1");
member.setTeamId(team.getId()); // 이 부분!
em.persist(member);
// 조회
Member findMember = em.find(Member.class, member.getId());

//Member와 Team이 연관관계가 없음
Team findTeam = em.find(Team.class, team.getId());

참조 대신 외래키를 그대로 넣은 경우입니다.

이렇게 하면 조회를 할 때 연관 관계가 없어서 2번 조회를 해야합니다.

따라서 객체를 테이블에 맞추어 모델링하는 것은 객체지향적인 방법이 아닙니다.

객체를 테이블에 맞추어 데이터 중심으로 모델링하면, 협력관계를 만들 수 없습니다.

  • 테이블은 외래키로 조인을 사용해서 연관된 테이블을 찾는다.
  • 객체는 참조를 사용해서 연관된 객체를 찾는다.
  • 이처럼 테이블과 객체는 큰 격차가 있고, 이 방법은 그 큰 격차를 무시한다.

2.단방향 매핑

객체의 참조(team)와 테이블의 외래키(TEAM_ID)를 매핑 (=연관관계 매핑)


@Entity
public class Member {
  
  @Id @GeneratedValue
  private Long id;
  
  @Column
  private String name;
  private int age;
  
  //@Column(name = "TEAM_ID")
  //private Long teamID;
  
  @ManyToOne
  @JoinColumn(name = "TEAM_ID")
  private Team team;
  
  ...
}

@Entity
public class Team {
  
  @Id @GeneratedValue
  private Long id;
  private String name;
  ...
}

하나의 팀에 여러명의 멤버가 들어가기 떄문에

멤버 입장에서는 다대일, 팁 입장에서는 일대다가 됩니다.

그래서 @ManyToOne이라는 어노테이션을 사용해야 합니다.


// 팀 저장
Team team = new Team();
team.setName("TeamA");
em.persist(team);

// 회원 저장
Member member = new Member();
member.setName("member1");
member.setTeam(team); // 단방향 연관관계 설정, 참조 저장
em.persist(member);


// 조회
Member findMember = em.find(Member.class, member.getId());

// 참조를 사용해서 연관관계 조회
Team findTeam = findMember.getTeam();

이 방식을 사용한다면 연관관계를 찾아서 한번에 조회가 가능합니다.

위의 코드에서 만약 @ManyToOne(fetch = FetchType.LAZY) 를 주면,

Member 객체만 조회하고 Team 객체는 실제 사용되는 시점에 DB를 조회합니다.(지연 로딩)

그렇다고 Team 객체가 null이 되면 에러가 나기 때문에 Team 객체는 프록시 객체(가짜 객체)가 들어갑니다.

디폴트는 (fetch = FetchType.EAGER)로 같이 조회합니다.

권장하는 것은 LAZY(지연 로딩)이다.

현업에서는 전부 LAZY로 바르고,

꼭 필요한 곳에서는 쿼리를 날리는 시점에 원하는 것을 미리 최적화해서 가져오는 방법을 쓰게 합니다.