JPA JoinColumns 사용시 주의 사항

Yoo Young-mo
7 min readSep 23, 2017

필자가 자바 ORM 표준 JPA 프로그래밍 책을 학습하며 겪었던 일이다.

예제 7.10 @EmbeddedId 식별 관계 매핑 하기’ 를 따라 하다가

자바 ORM 표준 JPA 프로그래밍 7장 고급 매핑을 일부분으로 @EmbeddedId 로 식별 관계 매핑하는 예제를 따라 하던 중 프로그래밍 오류는 아니지만 논리적 오류를 만났다.

출처 : 자바 ORM 표준 JPA 프로그래밍
@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에서 흔적을 찾을 수 있었다.

http://stackoverflow.com/questions/11342733/why-changing-the-order-of-the-joincolumn-hibernate-return-a-correct-or-incorrec

무엇이 문제인가?

@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 매핑을 하다보면 이런일이 자주 발생 한다.

--

--