JPA JoinColumns 사용시 주의 사항
필자가 자바 ORM 표준 JPA 프로그래밍 책을 학습하며 겪었던 일이다.
예제 7.10 @EmbeddedId 식별 관계 매핑 하기’ 를 따라 하다가
자바 ORM 표준 JPA 프로그래밍 7장 고급 매핑을 일부분으로 @EmbeddedId 로 식별 관계 매핑하는 예제를 따라 하던 중 프로그래밍 오류는 아니지만 논리적 오류를 만났다.
@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;
private String name;
...
}
@Embeddable
public class ChildId implements Serializable {
@Column(name = "CHILD_ID")
private String id;
private String parentId;
...
}
@Entity
public class GrandChild {
@EmbeddedId
private GrandChildId id;
@MapsId("childId")
@ManyToOne
@JoinColumns({
@JoinColumn(name = "PARENT_ID"),
@JoinColumn(name = "CHILD_ID")
})
private Child child;
private String name;
...
}
@Embeddable
public class GrandChildId implements Serializable {
private ChildId childId;
@Column(name = "GRANDCHILD_ID")
private String id;
...
}
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
transaction.begin();
Parent parent = new Parent();
parent.setId("parentId");
parent.setName("부모");
em.persist(parent);
ChildId childId = new ChildId();
childId.setId("childId");
childId.setParentId("parentId");
Child child = new Child();
child.setId(childId);
child.setParent(parent);
child.setName("자식");
em.persist(child);
GrandChildId grandChildId = new GrandChildId();
grandChildId.setId("grandchildid");
grandChildId.setChildId(childId);
GrandChild grandChild = new GrandChild();
grandChild.setId(grandChildId);
grandChild.setChild(child);
grandChild.setName("손자");
em.persist(grandChild);
transaction.commit();
em.close();
위의 코드를 실행 후 테이블에 삽입된 데이터는 아래와 같다.
무엇인가 이상하지 않은가?
GRAND_CHILD 테이블에 PARENT_ID 컬럼과 CHILD_ID 컬럼 값이 바뀌어 삽입되었다.
혹시나 다른 사람들도 같은 경험을 했나 검색해 보았더니 역시나 StackOverFlow에서 흔적을 찾을 수 있었다.
무엇이 문제인가?
@JoinColumns 를 사용시에는 referencedColumnName을 지정하지 않으면 INSERT시 @JoinColumns의 @JoinColumn 선언 순서에 따라 값이 바뀌어 삽입 되어 문제가 발생할 수 도 있다.
@JoinColumns({
@JoinColumn(name = "PARENT_ID"),
@JoinColumn(name = "CHILD_ID")
})
실제로 @JoinColumns JavaDoc을 확인해 보면 해당 내용이 언급되어 있다.
어떻게 해결해야 하나
먼저 미봉책으로 @JoinColumn 선언 순서를 바꾸어 주면 된다.
@Entity
public class GrandChild {
@EmbeddedId
private GrandChildId id;
@MapsId("childId")
@ManyToOne
@JoinColumns({
@JoinColumn(name = "CHILD_ID"),
@JoinColumn(name = "PARENT_ID")
})
private Child child;
private String name;
...
}
하지만 미봉책이므로 @JoinColumns JavaDoc에서 권고하는 데로 referencedColumnName을 지정 하면 된다.
@Entity
public class GrandChild {
@EmbeddedId
private GrandChildId id;
@MapsId("childId")
@ManyToOne
@JoinColumns({
@JoinColumn(name = "PARENT_ID", referencedColumnName = "PARENT_ID"),
@JoinColumn(name = "CHILD_ID", referencedColumnName = "CHILD_ID")
})
private Child child;
private String name;
결론
@JoinColumns 를 사용시에는 @JoinColumn의 referencedColumnName을 지정 하자.
마지막으로 여담이지만 단순이 책 예제로만 치부할 수 없는 것이 실무에서 JPA로 Entity 매핑을 하다보면 이런일이 자주 발생 한다.