ex) N: 멤버, 1: 팀
다대일 단 방향에서는 외래키를 가지는 엔티티가 "연관관계의 주인"이 된다.
@Entity
public class NClass{
@Id @GeneratedValue
@column(name = "N_ID")
private Long id;
private String name;
@ManyToOne
@JoinColumn(name = "ONE_ID") //*****
private OneClass oneclass;
}
@Entity
public class OneClass{
@Id @GeneratedValue
@column(name = "ONE_ID")
private Long id;
private String name;
}
"일(1) 엔티티"가 "다(N) 엔티티"를 외래키로 관리하게 된다.
@Entity
public class NClass{
@Id @GeneratedValue
@column(name = "N_ID")
private Long id;
private String name;
@ManyToOne
@JoinColumn(name = "ONE_ID")
private OneClass oneclass;
public void setOne(OneClass oneclass){ // Nclass와 무한루프 방지
this.oneclass = oneclass;
if(!oneclass.getN().contains(this)){
oneclass.getN().add(this);
}
}
}
@Entity
public class OneClass{
@Id @GeneratedValue
@column(name = "ONE_ID")
private Long id;
private String name;
@OneToMany(mappedBy = "oneclass") //*****
private List<NClass> nClass = new ArrayList<NClass>();
public void addN(Oneclass nClass){ // OneClass와 무한루프 방지
this.nClass.add(oneclass);
if(nClass.getOneClass() != this){
nClass.setOneclass(this);
}
}
}
- 연관관계 매핑에서 외래키를 관리하는 곳은 항상 ManyToOne을 가진 칼럼의 엔티티
= 다(N) 엔티티- 양방향 연관관계에서는 항상 서로를 참조시켜야함. (편의 메서드를 작성하여 양쪽다 객체 관점에서 데이터를 처리)
JPA 2.0 부터 지원
@Entity
public class NClass{
@Id @GeneratedValue
@column(name = "N_ID")
private Long id;
private String name;
}
@Entity
public class OneClass{
@Id @GeneratedValue
@column(name = "ONE_ID")
private Long id;
private String name;
@OneToMany //*****
@JoinColumn(name = "N_ID")
private List<NClass> nClass = new ArrayList<NClass>();
}
오히려 다 쪽에서 외래키를 관리하게된다.
- 일대다 단 방향 관계를 매핑할 때는 @JoinColumn을 명시 해야함.
- 매핑 관계에서 외래키가 반대쪽 엔티티에 저장되어 있다.
(N에 원래 있어야하는데 1엔티티에서 관리)- INSERT 외에 UPDATE문 까지 추가로 쿼리가 만들어지게 됨.
다대일 양방향을 사용하도록 하는 것을 권장함..!
-> 객체 지향 관점에서 더 편함.
- 주 테이블에서 외래키를 단방향으로 가질 수는 있다.
-> DB 테이블 관리 관점에서 더 편함. (일대다) 테이블로 전환하기 더 용이함.
- 일대일 관계에서는 대상테이블에서 주 테이블로 단방향 매핑이 존재하지 않음..
- 반드시 양방향을 통해 관계를 형성하여야 한다.
@Entity
public class MainClass{
@Id @GeneratedValue
@Column(name = "MAIN_ID")
private Long id;
private String name;
@OneToOne(mappedBy = "main")
private SubClass sub;
}
@Entity
public class SubClass{
@Id @GeneratedValue
@Column(name = "SUB_ID")
private Long id;
private String name;
@OneToOne
@JoinColumn(name = "MAIN_ID")
private MainClass main;
프록시를 사용할 때 외래키를 직접 관리하지 않는 MainClass (mapped By를사용하는 매핑 관계의 주인이 아닌 엔티티)는 지연로딩이 되지만, SubClass는 즉시 로딩이 됨.
ex)
@ManyToMany
@JoinTable(name = "CONN_TABLE",
joinColumns = @JoinColumn(name="My_PK"),
inserveJoinColumns = @JoinColumn(name="Target_FK"))
private List<Target> targets = new ArrayList<Target>();
@Entity
public class Member{
@Id @Column(name = "MEMEBER_ID")
private String id;
@OneToMany(mappedBy = "member")
private List<MemberProduct> MemberProducts;
}
@Entity
public class Product{
@Id @Column(name = "PRODUCT_ID")
private String id;
private String name;
}
@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 int orderPrice;
}
public class MemberProductId implements Serializable{
private String member;
private String product;
@Override
equals(), hashCode();
}
- 복합 기본키를 사용할 시에 식별자를 위한 클래스를 반드시 만들어야함
- 복합 기본키 식별자 클래스는 Serializable을 인터페이스로 사용.
- 반드시 public 클래스로 지정되어야 함. @EmbeddedId 어노테이션으로 대체 가능함.
- 위와 같이 복합기본키를 특정 엔티티의 기본키로 사용하는 상태를 식별 관계라고 함.
MemberProductId mpId = new MemberProductId();
mpId.setMember("member1");
mpId.setProduct("productA");
MemberProduct mp = em.find(MemberProduct.class, memberProductId);
복합키말고 해당 엔티티에 기본키를 새로 쥐어주고, 타 테이블 엔티티를 그냥 외래키 참조 형태로 구현하면 더 간단하다.
@Entity
public class Order{
@Id @GeneratedValue
@Column(name = "ORDER_ID")
private Long id;
@ManyToOne
@JoinCOlumn(name = "MEMBER_ID")
private Member member;
@ManyToOne
@JonColumn(name = "PRODUCT_ID")
private Product product;
private int orderAmount;
...
}
Long orderId = 1L;
Order order = em.find(Order.class, orderId);
외래키 관계를 이렇게 쉽게 정의하다니 편해보인다...
이전에 했던 Jdbc를 사용한 프로젝트를 이렇게 다 변환해서 매핑하면 얼마나 편할지 상상이 안감..