티스토리 뷰
다대일[N:1]
다대일 단방향
회원 엔티티
@Entity
public class Member {
@Id
@Column(name = "member_id")
@GeneratedValue
private Long id;
@ManyToOne
@JoinColumn(name = "team_id")
private Team team;
}
팀 엔티티
@Entity
public class Team {
@Id
@Column(name = "team_id")
@GeneratedValue
private Long id;
private String name;
}
- @ManyToOne 어노테이션을 사용하여 다대일 단방향 매핑
- @JoinColumn 어노테이션을 사용하여 회원 테이블의 Team_Id와 회원 엔티티의 team 필드를 매핑
다대일 양방향
@OneToMany(mappedBy = "team")
private List<Member> mebers = new ArrayList<>();
- 팀 엔티티에 @OneToMany 어노테이션을 추가하여 양방향 매핑
- mappedBy 속성을 사용하여 연관관계 주인 설정
- 양방향은 항상 외래키가 있는 쪽이 연관관계의 주인이다
- 양방향 연관관계는 항상 서로를 참조해야한다
일대다[1:N]
일대다 단방향
@Entity
public class Team {
@Id
@Column(name = "team_id")
@GeneratedValue
private Long id;
private String name;
@OneToMany
@JoinColumn(name = "team_id")
private List<Member> mebers = new ArrayList<>();
}
- @JoinColumn을 반드시 사용해야한다.
- 반대편 테이블의 외래키를 관리하기 때문
- 그렇지 않으면 중간에 테이블을 하나 추가하는 조인 테이블 방식을 사용한다.
- 1:N 단방향의 매핑의 단점
- 엔티티가 매핑한 외래키가 다른 테이블에 있기 때문에 발생한다
- 다른 테이블에 외래키가 있으므로 연관관계 처리를 위한 Update SQL을 추가로 실행해야한다
- 때문에 성능 문제가 발생하고 관리도 어려워진다
- 일대다 단방향 매핑보단 다대일 양방향 매핑을 권장한다
일대다 양방향
사실상 일대다 양방향 매핑은 존재하지 않는다(@OneToMany는 주인이 될 수 없다)
@ManyToOne
@JoinColumn(insertable = false, updatable = false)
private Team team;
- 일대다 매핑 반대편에 같은 외래 키를 사용하는 다대일 단방향 매핑을 읽기 전용으로 추가하여 양방향처럼 보이게 할 수 있다
- 하지만 일대다 단방향 매핑이 가지는 단점을 그대로 가지므로 다대일 양방향 매핑을 권장한다.
일대일[1:1]
- 주 테이블이나 대상 테이블 중 어느곳에 외래키를 둘것인지 선택해야 한다.
- 외래키에 데이터베이스 유니크 제약 조건을 추가해야 한다.
주 테이블에 외래키가 있는 경우
일대일 단방향
회원 엔티티
@Entity
public class Member {
@Id
@Column(name = "member_id")
@GeneratedValue
private Long id;
private string username;
@OneToOne
@JoinColumn(name = "locker_id")
private Locker locker;
}
락커 엔티티
@Entity
public class Locker {
@Id
@Column(name = "locker_id")
@GeneratedValue
private Long id;
private String name;
}
- 회원 테이블(= 주 테이블)의 외래키 locker_id와 locker 필드 매핑
- 다대일 단방향과 비슷한 형태
일대일 양방향
@OneToOne(mappedBy = "locker")
private Member member;
- 반대 엔티티에 @OneToOne 어노테이션 추가후 mapped by 속성을 통해 연관관계 주인 설정
대상 테이블에 외래키가 있는 경우
일대일 단방향
단방향 관계는 지원하지 않는다
일대일 양방향
@OneToOne
@JoinColumn(name = "Member_id")
private Member member;
@OneToOne(mapped by = "member")
private Locker locker;
- 일대일 주 테이블에 외래키 양방향 매핑과 방법이 같다
- 외래키의 위치가 뒤바뀜에 따라 연관관계의 주인도 반대로 뒤바껴진다
- 프록시를 사용할 때 외래 키를 직접 관리하지 않는 일대일 관계는 즉시 로딩된다
- 1:N의 경우 배열의 형태로 프록시를 감싸기 때문에 문제가 없지만
- 1:1은 객체가 null인 경우 null을 프록시로 감쌀 수 없기 때문
다대다[N:N]
- 관계형 데이터베이스는 정규화된 테이블 2개로 다대다 관계를 표현할 수 없다
- 그래서 중간에 연결 테이블을 추가하여 일대다, 다대일 관계로 풀어내야 한다
- 객체는 테이블과 다르게 객체 2개로 다대다 관계를 만들 수 있다
다대다 단방향
@Entity
public class Member {
@Id
@GeneratedValue
@Column(name = "member_id")
private Long id;
@ManyToMany
@JoinTable(name = "member_product",
joinColumns = @JoinColumn(name = "member_id"),
inverseJoinColumns = @JoinColumn(name = "product_id"))
private List<Product> productList = new ArrayList<>();
}
- @JoinTable 어노테이션을 사용하여 연결 테이블을 매핑해준다
다대다 양방향
@Entity
public class Product {
@Id
@GeneratedValue
@Column(name = "product_id")
private Long id;
@ManyToMany(mappedBy = "productList")
private List<Member> memberList = new ArrayList<>();
}
- 반대도 @ManyToMany 어노테이션을 통해 매핑하고 mapped by 속성을 통해 연관관계 주인을 지정한다
다대다에서의 연결 엔티티 사용
복잡한 비즈니스 로직으로 인해 연결 테이블에 칼럼을 추가하게 되면 @ManyToMany를 사용할 수 없게 된다.
왜냐면 각 회원, 상품 엔티티에서 추가한 칼럼들을 매핑할 수 없기 때문이다. 이러한 문제를 해결하기 위해선 연결 테이블에 대한
연결 엔티티를 만들고 엔티티 간의 관계를 다대일, 일대다 관계로 풀어야 한다.
이때 연결 엔티티의 기본키 생성 전략이 두가지가 있다
- 복합 기본 키 사용
- 새로운 기본 키 사용
복합 기본 키를 사용한 연결 엔티티 생성
회원 엔티티
@Entity
public class Member {
@Id
@GeneratedValue
@Column(name = "member_id")
private Long id;
private String memberName;
@OneToMany(mappedBy = "member")
private List<MemberProduct> memberProductList
}
상품 엔티티
@Entity
public class Product {
@Id
@GeneratedValue
@Column(name = "product_id")
private Long id;
private String productName;
@OneToMany(mappedBy = "product")
private List<MemberProduct> memberProductList
}
회원-상품 엔티티 (연결 엔티티)
@Entity
@IdClass(MemberProductId.class)
public class MemberProduct {
@Id
@ManyToOne
@JoinColumn(name = "member_id")
private Member member;
@Id
@ManyToOne
@JoinColumn(name = "product_id")
private Product product;
private int orderAmount;
private LocalDate orderDate;
}
- 회원 엔티티와 연결 엔티티를 일대다, 다대일 양방향 관계로 풀어주었다
- 상품 엔티티에서 객체 그래프 탐색이 필요 없을 것을 예상하여 관계를 없애주었다
- 연결 엔티티에서 복합 기본 키를 사용하여 기본 키를 매핑했다
- 기본 키를 매핑하는 @Id와 외래 키를 매핑하는 @joinColumn을 동시에 사용하여 기본키 + 외래키를 한번에 매핑
- @IdClass를 사용해서 복합 기본 키를 매핑
새로운 기본 키를 사용한 연결 엔티티 생성
회원 엔티티
@Entity
public class Member {
@Id
@GeneratedValue
@Column(name = "member_id")
private Long id;
private String memberName;
@OneToMany(mappedBy = "member")
private List<MemberProduct> memberProductList
}
상품 엔티티
@Entity
public class Product {
@Id
@GeneratedValue
@Column(name = "product_id")
private Long id;
private String productName;
@OneToMany(mappedBy = "product")
private List<MemberProduct> memberProductList
}
회원-상품 엔티티(연결 엔티티)
@Entity
public class MemberProduct {
@Id
@GeneratedValue
@Column(name = "order_id")
private Long id;
@ManyToOne
@JoinColumn(name = "member_id")
private Member member;
@ManyToOne
@JoinColumn(name = "product_id")
private Product product;
private int orderAmount;
private LocalDate orderDate;
}
- 대리키를 사용함으로써 앞의 복합 키를 사용하는 것보다 매핑이 단순해진다
'BackEnd > JPA 스터디' 카테고리의 다른 글
[JPA 스터디 4주차] - 값 타입 (0) | 2021.07.22 |
---|---|
[JPA 스터디 3주차] - 프록시와 연관관계 정리 (0) | 2021.07.14 |
[JPA 스터디 2주차] - 엔티티 매핑 (0) | 2021.07.05 |
[JPA 스터디 1주차] - 영속성 관리 (0) | 2021.06.26 |
[JPA 스터디 1주차] - JPA 시작 (0) | 2021.06.24 |