연관관계의 주인
- FK를 갖고 있는 곳!
- mappedBy가 없는 곳!
상황별 Annotation 정리
- @ManyToOne
- 주인
- @ManyToOne
- @JoinColumn(name = "COLUMN_NAME")
- !주인
- @OneToMany(mappedBy = "ownerField")
- 주인
@OneToMany (NOT RECOMMENDED)- !주인
- @OneToMany
- @JoinColumn(name = "TEAM_ID")
- 주인
- @ManyToOne
- @JoinColumn( name = "COLUMN_NAME", insertable = false, updatable = false)
- !주인
- @OneToOne
- 주인
- @OneToOne
- @JoinColumn(name = "COLUMN_NAME")
- !주인
- @OneToOne(mappedBy = "ownerField")
- 주인
@ManyToMany (NOT RECOMMENDED)- 주인
- @ManyToMany
- @JoinColumn(name = "MID_TABLE_NAME", joinColumns = "MY_COLUMN_NAME", inverseJoinColumns = @JoinColumn(name = "YOUR_COLUMN_NAME") )
- !주인
- @ManyToMany(mappedBy = "ownerField")
- 주인
- → @ManyToMany의 대안 : @ManyToOne 두개 사용!
@Entity
class Member{
...
@ManyToOne
@JoinColumn(name = "TEAM_ID")
private Team team;
...
}
class Team{
... // member와 관련된 필드가 없다.
}
- @ManyToOne : 다대일 관계에서 '다' 에 붙임
- @JoinColumn : 상대방 column 지정한다. 생략 가능
- name : 테이블에서 FK column 이름 / 생략시
{상대방 table명}_{상대방 PK 컬럼명}
- name : 테이블에서 FK column 이름 / 생략시
@Entity
class Member{
...
@ManyToOne
@JoinColumn(name = "TEAM_ID")
private Team team;
...
// 연관관계 편의 메소드
public void setTeam(Team team) {
// 기존 team 리스트에서 제거
if (this.team != null) {
this.team.getMembers().remove(this);
}
this.team = team;
// 새로운 team에 추가
if (this.team != null && !team.getMembers().contains(team)) {
team.getMembers().add(this);
}
}
...
}
class Team{
...
@OneToMany(mappedBy = "team") // mappedBy = 반대편 @ManyToOne의 변수명
private List<Member> members = new ArrayList<Member> ();
public void addMember(Member member) {
this.members.add(member);
if (member.getTeam() != this) { // 무한루프 체크
member.setTeam(this);
}
}
...
}
- 연관관계의 주인 설정 == 항상 FK가 있는 곳!
- @OneToMany : 다대일 에서 '일'에 붙이는 Annotation
- mappedBy : 반대편 @ManyToOne의 변수명
- 연관관계 편의 메소드 : 객체가 테이블과 같이 항상 서로를 참조하게 하기 위한 메소드
- 이전 연결 객체로부터 데이터 삭제 필요! (
this.team.getMembers().remove(this)
) - 무한루프 체크 필요
- 이전 연결 객체로부터 데이터 삭제 필요! (
[!note] 일대다 보다는 다대일이 좋다!
class Team{
...
@OneToMany
@JoinColumn(name = "TEAM_ID")
private List<Member> members = new ArrayList<Member> ();
...
}
@Entity
class Member{
...
}
- "일" 에서 JoinColumn을 사용할 경우, FK는 "다" 테이블에 저장된다.
- @JoinColumn : 다대일 에서와 달리생략 불가능하다
- name : Member 테이블에서 FK column 이름
class Team{
...
@OneToMany
@JoinColumn(name = "TEAM_ID")
private List<Member> members = new ArrayList<Member> ();
...
}
@Entity
class Member{
...
@ManyToOne
@JoinColumn(name = "TEAM_ID", insertable = false, updatable = false)
private Team team;
...
}
- "다" 엔티티에도 @JoinColumn을 추가한다.
- 이때 Team과 Member 모두에서 FK를 관리하므로 문제 발생
- ->
insertable = false, updatable = false
설정 필요!
- ->
@Entity
class Member{
...
@OneToOne
@JoinColumn(name = "LOCKER_ID")
private Locker locker;
...
}
class Locker{
...
}
@Entity
class Member{
...
@OneToOne
@JoinColumn(name = "LOCKER_ID")
private Locker locker;
...
}
class Locker{
...
@OneToOne(mappedBy = "locker")
private Member member;
...
}
- -> 사용 불가능
@Entity
class Member{
...
@OneToOne(mappedBy = "member")
private Locker locker;
...
}
class Locker{
...
@OneToOne
@JoinColumn(name = "MEMBER_ID")
private Member member;
...
}
@Entity
class Member{
...
@ManyToMany
@JoinColumn(name = "MEMBER_PRODUCT",
joinColumns = "MEMBER_ID",
inverseJoinColumns = @JoinColumn(name = "PRODUCT_ID")
)
private List<Product> products = new ArrayList<>();
...
}
@Entity
class Product{
...
}
- @ManyToMany
- @JoinColumn
- name : 연결 테이블 이름 지정
- joinColumns : 현재 방향 엔티티(회원)의 컬럼명
- inverseJoinColumns : 반대 방향 엔티티(상품)의 컬럼명
@Entity
class Member{
...
@ManyToMany
@JoinColumn(name = "MEMBER_PRODUCT",
joinColumns = "MEMBER_ID",
inverseJoinColumns = @JoinColumn(name = "PRODUCT_ID")
)
private List<Product> products = new ArrayList<>();
// 연관관계 편의 메소드
public void addProduct(Product product) {
...
products.add(product);
product.getMembers().add(this);
}
...
}
@Entity
class Product{
...
@ManyToMany(mappedBy = "products")
private List<Member> products = new ArrayList<>();
...
}
- mappedBy 로 연관관계의 주인 설정
- 연관관계의 주인 = Member
@Entity
class Member{
...
@OneToMany(mappedBy = "member")
private List<MemberProduct> memberProducts;
...
}
@Entity
class Product{
...
}
@Entity
@IdClass(MemberProductId.class)
class MemberProduct{
@Id
@ManyToOne
@JoinColumn(name = "MEMBER_ID")
private Member member;
@Id
@ManyToOne
@JoinColumn(name = "PRODUCT_ID")
private Product product;
// 추가 필드
private int orderAmount;
...
}
class MemberProductId implements Serializable{
private String member;
private String product;
@Override
public boolean equals(Object o) {...}
@Override
public int hachCode(Object o) {...}
}
@IdClass(MemberProductId.class)
- 복합 기본 키 를 만들기 위한 클래스
- 별도의 식별자 클래스로 분리, Serializable 구현
- 기본 생성자, equals, hashCode 필요
@Entity
class Order{
@Id @GeneratedValue
@Column(name = "ORDER_ID")
private Long Id;
@Id
@ManyToOne
@JoinColumn(name = "MEMBER_ID")
private Member member;
@Id
@ManyToOne
@JoinColumn(name = "PRODUCT_ID")
private Product product;
// 추가 필드
private int orderAmount;
...
}
방법 1 식별 관계 : 받아온 식별자를 기본키 + 외래 키로 사용한다.
- 데이터를 표현하는 더 나은 방식
- 새로운 식별자(Auto increment)는 해당 column 데이터와 전혀 무관한 데이터이기 때문에 불필요한 데이터이다.
- 중복 데이터 발생 가능성..!!
- 데이터의 조회/수정/삭제 성능 개선
방법 2 비식별 관계 : 받아온 식별자는 외래 키로만 사용하고 새로운 식별자 추가
- 생성이 간편
- 영구적으로 사용 가능하다
- 비즈니스에 의존하지 않는다
- JPA 코드 구현이 간편하다.