본문 바로가기

Back-End/JPA

JPA 연관관계 Mapping

단방향

단방향 연관관계는 객체 간의 관계가 한쪽에서만 참조될 수 있는 관계를 말함. 설정이 단순하고 유지 관리가 쉬우며 불필요한 데이터 접근을 방지할 수 있다.

1. DB 중심

  • FK 값은 Tutor가 가지고 있다.
    • Tutor만 참조할 수 있다.
    • N:1, 다대일 연관관계에 가장 많이 사용된다.
    • 여러명(N)의 Tutor가 어떤 Company(1)에 소속 되어있는지 설정할 수 있다.
  • 코드
@Entity
@Table(name = "company")
public class Company {
		@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
		private Long id;
		
		private String name;
		
		// 기본 생성자, getter/setter
}
@Entity
@Table(name = "company")
public class Company {
		@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
		private Long id;
		
		private String name;
		
		// 기본 생성자, getter/setter
}
// Company 생성 및 persist
Company company = new Company("toss");
em.persist(company);

// Tutor 생성, setCompanyId, persist
Tutor tutor = new Tutor("PBC");
tutor.setCompanyId(company.getId());
em.persist(tutor);

// IDENTITY 전략을 사용하면 persist()이후 PK를 바로 조회할 수 있다.
Tutor findTutor = em.find(Tutor.class, tutor.getId());

// 조회한 Tutor의 CompanyId로 Company 조회
Long companyId = findTutor.getCompanyId();
Company findCompany = em.find(Company.class, companyId);
  • 객체 지향적인 코드를 작성할 수 없다.
  • Java Collection을 사용하는 것처럼 tutor.getCompany()를 사용하지 못한다.

2. 객체 지향

  • 객체는 다른 객체를 참조한다.
  • N : 1 관계는 @ManyToOne, @JoinColumn을 사용한다.
  • 코드
@Entity
@Table(name = "tutor")
public class Tutor {
		@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
		private Long id;
		
		private String name;
		// N:1 단방향 연관관계 설정
		@ManyToOne
		@JoinColumn(name = "company_id")
		private Company company;
		
		// 기본 생성자, getter/setter
}
// Company 생성 및 persist
Company company = new Company("sparta");
em.persist(company);

// Tutor 생성, setCompany, persist
Tutor tutor = new Tutor("wonuk");
tutor.setCompany(company);
em.persist(tutor);

// IDENTITY 전략을 사용하면 persist()이후 PK를 바로 조회할 수 있다.
Tutor findTutor = em.find(Tutor.class, tutor.getId());

// 조회한 Tutor의 Company 조회
Company findCompany = findTutor.getCompany();
  • Tutor의 FK와 Company의 PK를 @JoinColumn 으로 매핑한다.
  • JavaCollection을 사용하는 것처럼 tutor.getCompany() 를 사용할 수 있다.

양방향 연관관계

양방향 연관관계는 객체 간의 관계가 양쪽에서 서로를 참조할 수 있는 관계를 의미한다. 이를 통해 양쪽에서 데이터를 쉽게 접근할 수 있지만 관계를 관리할 때 한쪽에서만 연관관계를 설정하거나 삭제하지 않도록 주의가 필요하다.

테이블

  • 테이블에 변화는 없다.
    • Tutor 테이블의 FK로 Company 테이블에 JOIN
    • Company 테이블의 PK로 Tutor 테이블에 JOIN
  • 사실상 테이블의 연관관계에는 방향의 개념이 없다.

객체

  • 코드
@Entity
@Table(name = "tutor")
public class Tutor {
		@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
		private Long id;
		
		private String name;
		// N:1 단방향 연관관계 설정
		@ManyToOne
		@JoinColumn(name = "company_id")
		private Company company;
		
		// 기본 생성자, getter/setter
}
@Entity
@Table(name = "company")
public class Company {
		@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
		private Long id;
		
		private String name;
		
		// null을 방지하기 위해 ArrayList로 초기화 한다.(관례)
		@OneToMany(mappedBy = "company")
		private List<Tutor> tutors = new ArrayList<>();
		
		// 기본 생성자, getter/setter
}
  • 양방향 연관관계 설정을 위해 mappedBy 속성을 설정한다.
    • Tutor의 company 필드와 매핑된다.
// Company 생성 및 persist
Company company = new Company("sparta");
em.persist(company);

// Tutor 생성, setCompany, persist
Tutor tutor = new Tutor("wonuk");
tutor.setCompany(company);
em.persist(tutor);

// IDENTITY 전략을 사용하면 persist()이후 PK를 바로 조회할 수 있다.
Tutor findTutor = em.find(Tutor.class, tutor.getId());

// 조회한 Tutor의 Company 조회
Company findCompany = findTutor.getCompany();

// Company에 속한 Tutor List 조회
List<Tutor> tutors = findCompany.getTutors();

양방향 연관관계의 주인

mappedBy는 JPA 양방향 연관관계 설정 시 사용되는 속성으로 두 엔티티 간의 관계에서 연관관계의 주인이 아닌 쪽에 선언한다.
이를 통해 외래 키 관리 책임을 주인 엔티티에 두고 매핑이 중복되지 않도록 한다.

  1. Tutor의 Company를 수정할 때 FK가 수정
  2. Company의 Tutor를 수정할 때 FK가 수정
    • Tutor가 새로운 Company를 간다면?
      • Tutor의 참조값 Company 수정
      • Company의 참조값 List tutors 수정
      • DB입장에서는 FK만 수정되면 된다.
    • 결국, 둘 중 하나로만 외래 키를 관리해야 한다.
  • 양방향 연관관계 규칙

  1. 두개의 Entity 중 하나를 연관관계의 주인으로 설정 해야한다.
  2. 연관관계의 주인은 mappedBy 속성을 사용하지 않는다.
  3. 연관관계의 주인이 아니라면 mappedBy 속성을 사용한다.
  4. 연관관계의 주인이 아니라면 조회만 가능하다.
  5. 연관관계의 주인만 외래 키를 관리(등록, 수정)할 수 있다.
  • 연관관계의 주인 선정 기준
    • 항상 FK가 있는 곳을 연관관계의 주인으로 지정한다.
    • 양방향 1 대 N 관계에서 주인을 N 쪽에 두는 것이 일반적이다. 이유는 N 쪽에 외래키가 존재하기 때문에 N 쪽에서 외래키를 관리하는 것이 자연스럽다.

실무에서의 사용 전략

  • 단방향 연관관계:
    • 권장: 대부분의 경우 단방향을 기본으로 설계.
    • 이유: 간단하고 성능상 유리.
  • 양방향 연관관계:
    • 필요할 때만 사용.
    • 예: 부모-자식 관계에서 자식 데이터를 자주 조회해야 하는 경우.

연관관계 매핑 실무 팁

1. 연관관계 주인 설정

  • 연관관계 주인은 외래 키를 관리하는 엔티티로 설정.
  • 양방향일 때 주인만 수정해야 JPA가 외래 키를 제대로 관리.

2. 지연로딩 기본 설정

  • 연관관계 필드에는 기본적으로 Lazy Loading을 설정해 불필요한 데이터 로딩 방지.
@ManyToOne(fetch = FetchType.LAZY)
private Member member;

3. 데이터 설계 우선

  • 객체 연관관계가 복잡할수록 데이터베이스 설계를 먼저 고민
  • 일단 단방향 매핑만으로 이미 연관관계 매핑은 완료 되므로 단방향 매핑을 잘 하고 양방향은 필요할 때 추가하는 쪽으로

4. 필요하지 않으면 양방향을 피하자

  • 양방향은 설계와 유지보수의 복잡성을 증가시키므로 꼭 필요한 경우에만 사용.