부모의 PK를 받아 FK이자 PK로 사용합니다.
단점이 너무 많아요오….
부모의 PK가 FK의 역할만 합니다.
자손 테이블은 PK에 비즈니스와 관련 없는 대리키를 사용하는 경우가 대부분입니다.
단일 키일때는 보통 자바의 기본 타입을 사용하지만 복합키일 땐 별도의 식별자 클래스를 만들고 Serializable
을 구현해야 합니다.
왜냐하면 JPA는 영속성 컨텍스트에 엔티티를 보관할 때 엔티티의 식별자를 키로 사용하는데 이 식별자를 구분하기 위해 equals
와 hashCode
를 사용해 동등성 비교를 합니다.
@Entity
@IdClass(ParentId.class)
public class Parent {
@Id
@Column(name = "PARENT_ID1")
private String id1;
@Id
@Column(name = "PARENT_ID2")
private String id2;
private String name;
}
public class ParentId implements Serializable {
private String id1;
private String id2;
public ParentId() {
}
//getter&setter
//equals&hashCode
}
@Entity
public class Child{
@Id @GeneratedValue
@Column(name = "CHILD_ID")
private Long id;
@ManyToOne
@JoinColumns({
@JoinColumn(name = "PARENT_ID1", referencedColumnName = "PARENT_ID1"),
@JoinColumn(name = "PARENT_ID2", referencedColumnName = "PARENT_ID2")
})
private Parent parent;
private String name;
}
@Entity
public class Parent {
@EmbeddedId
private ParentId id;
private String name;
}
@Embeddable
public class ParentId implements Serializable {
@Column(name = "PARENT_ID1")
private String id1;
@Column(name = "PARENT_ID2")
private String id2;
public ParentId() {
}
//getter&setter
//equals&hashCode
}
Parent parent = new Parent();
ParentId parentId = new ParentId();
parentId.setId1("myId1");
parentId.setId2("myId2");
parent.setId(parentId);
parent
의 ID를 **@Id
를 이용해 PK로 매핑하고 **@ManyToOne
을 이용해 연관관계(FK) 매핑을 합니다.child
의 복합키 클래스는 둘 다 String으로 id를 매핑하면 되지만
**grand_child
의 복합키 클래스는 하나는 child
의 복합키 클래스를 받아야 합니다.**@Entity
public class Parent {
***@Id @Column(name = "PARENT_ID")
private String id;***
private String name;
}
@Entity
@IdClass(ChildId.class)
public class Child {
***@Id
@ManyToOne
@JoinColumn(name = "PARENT_ID")
private Parent parent;***
@Id @Column(name = "CHILD_ID")
private String childId;
private String name;
}
@Entity
@IdClass(GrandChildId.class)
public class GrandChild {
***@Id
@ManyToOne
@JoinColumns({
@JoinColumn(name = "PARENT_ID"),
@JoinColumn(name = "CHILD_ID")
})
private Child child;***
@Id @Column(name = "GRANDCHILD_ID")
private String grandChildId;
private String name;
}
public class ChildId implements Serializable {
private String parent;
private String childId;
}
public class GrandChildId implements Serializable {
***private ChildId child;***
private String grandChildId;
}
// 부모 생성
Parent parent = new Parent();
parent.setName("parentName1");
em.persist(parent);
// 자식 생성
Child child = new Child();
child.setName("childName1");
child.setParent(parent);
em.persist(child);
// 손주 생성
GrandChild grandChild = new GrandChild();
grandChild.setName("grandChildName1");
grandChild.setChild(child);
em.persist(grandChild);
tx.commit();
em.clear();
// 부모 조회
Parent findParent = em.find(Parent.class, parent.getId());
// 자식 조회
ChildId childId = new ChildId();
childId.setParent(findParent.getId());
childId.setChildId(child.getChildId());
Child findChild = em.find(Child.class, childId);
// 손주 조회
GrandChildId grandChildId = new GrandChildId();
grandChildId.setChild(childId);
grandChildId.setGrandChildId(grandChild.getGrandChildId());
GrandChild findGrandChild = em.find(GrandChild.class, grandChildId);
query log
Hibernate:
select
grandchild0_.PARENT_ID as parent_i0_1_0_,
grandchild0_.CHILD_ID as child_id0_1_0_,
grandchild0_.GRANDCHILD_ID as grandchi1_1_0_,
grandchild0_.PARENT_ID as parent_i3_1_0_,
grandchild0_.CHILD_ID as child_id4_1_0_,
grandchild0_.name as name2_1_0_,
child1_.CHILD_ID as child_id1_0_1_,
child1_.PARENT_ID as parent_i2_0_1_,
child1_.name as name3_0_1_,
parent2_.PARENT_ID as parent_i1_2_2_,
parent2_.name as name2_2_2_
**from
GrandChild grandchild0_
inner join
Child child1_
on grandchild0_.PARENT_ID=child1_.CHILD_ID
and grandchild0_.CHILD_ID=child1_.PARENT_ID
inner join
Parent parent2_
on child1_.PARENT_ID=parent2_.PARENT_ID
where
grandchild0_.PARENT_ID=?
and grandchild0_.CHILD_ID=?
and grandchild0_.GRANDCHILD_ID=?**
@EmbeddedId
를 이용할 경우 @GeneratedValue
가 안됩니다.**@MapsId
를 이용해서 해당 키를 FK로도 쓰고 PK에도 매핑합니다.**@Entity
public class Parent {
@Id
@Column(name = "PARENT_ID")
private String id;
private String name;
}
@Entity
public class Child {
@EmbeddedId
private ChildId id;
**@MapsId("parentId")
@ManyToOne
@JoinColumn(name = "PARENT_ID")
private Parent parent; //ChildId 클래스의 parentId에 매핑**
private String name;
}
@Entity
public class GrandChild {
@EmbeddedId
private GrandChildId id;
**@MapsId("childId")
@ManyToOne
@JoinColumns({
@JoinColumn(name = "PARENT_ID"),
@JoinColumn(name = "CHILD_ID")
})
private Child child; //GrandChildId 클래스의 childId에 매핑**
private String name;
}
@Embeddable
public class ChildId implements Serializable {
@Column(name = "CHILD_ID")
private String childId;
**private String parentId; //@MapsId로 매핑된 키**
//noArgsConstructor
//getter&setter
//equals&hashCode
}
@Embeddable
public class GrandChildId implements Serializable {
@Column(name = "GRANDCHILD_ID")
private String grandChildId;
**private ChildId childId; //@MapsId로 매핑된 키**
//noArgsConstructor
//getter&setter
//equals&hashCode
}
// 부모 생성
Parent parent = new Parent();
parent.setId("parentId");
parent.setName("parent");
em.persist(parent);
// 자식 생성
Child child = new Child();
child.setId(new ChildId(parent.getId(), "childId"));
child.setName("child");
child.setParent(parent);
em.persist(child);
// 손주 생성
GrandChild grandChild = new GrandChild();
grandChild.setId(new GrandChildId(child.getId(), "grandChildId"));
grandChild.setName("grandChild");
grandChild.setChild(child);
em.persist(grandChild);
tx.commit();
em.clear();
// 부모 조회
Parent findParent = em.find(Parent.class, parent.getId());
// 자식 조회
Child child1 = em.find(Child.class, child.getId());
// 손주 조회
GrandChild grandChild1 = em.find(GrandChild.class, grandChild.getId());
query log
select
grandchild0_.PARENT_ID as parent_i0_1_0_,
grandchild0_.CHILD_ID as child_id0_1_0_,
grandchild0_.GRANDCHILD_ID as grandchi1_1_0_,
grandchild0_.PARENT_ID as parent_i3_1_0_,
grandchild0_.CHILD_ID as child_id4_1_0_,
grandchild0_.name as name2_1_0_,
child1_.CHILD_ID as child_id1_0_1_,
child1_.PARENT_ID as parent_i2_0_1_,
child1_.name as name3_0_1_,
parent2_.PARENT_ID as parent_i1_2_2_,
parent2_.name as name2_2_2_
**from
GrandChild grandchild0_
inner join
Child child1_
on grandchild0_.PARENT_ID=child1_.CHILD_ID
and grandchild0_.CHILD_ID=child1_.PARENT_ID
inner join
Parent parent2_
on child1_.PARENT_ID=parent2_.PARENT_ID
where
grandchild0_.PARENT_ID=?
and grandchild0_.CHILD_ID=?
and grandchild0_.GRANDCHILD_ID=?**
부모와 자손이 일대일 관계일때 부모의 PK가 자손의 PK가 되는 단일 키 식별관계를 만들 수 있습니다.
@Entity
public class Board {
@Id @GeneratedValue
@Column(name = "BOARD_ID")
private Long id;
private String title;
**@OneToOne(mappedBy = "board")
private BoardDetail boardDetail;**
//getter&setter
}
@Entity
public class BoardDetail {
**@Id
private Long boardId;
*@MapsId*
@OneToOne
@JoinColumn(name = "BOARD_ID")
private Board board;**
private String content;
//getter&setter
}
//게시글 등록
Board board = new Board();
board.setTitle("board1");
em.persist(board);
//게시글 작성
BoardDetail boardDetail = new BoardDetail();
boardDetail.setContent("board1 - content1");
boardDetail.setBoard(board);
em.persist(boardDetail);
tx.commit();
em.clear();
//게시글 찾기
Board findBoard = em.find(Board.class, board.getId());
//게시글 작성 내용 찾기
BoardDetail findBoardDetail = em.find(BoardDetail.class, ***board.getId()***);
Hibernate:
select
board0_.BOARD_ID as board_id1_0_0_,
board0_.title as title2_0_0_,
boarddetai1_.BOARD_ID as board_id1_1_1_,
boarddetai1_.content as content2_1_1_
from
Board board0_
left outer join
BoardDetail boarddetai1_
on board0_.BOARD_ID=boarddetai1_.BOARD_ID
where
board0_.BOARD_ID=?