연관관계

RDB의 핵심기능 중에 하나인 연관관계를 객체에 맵핑 시켜주어야 하는데 이때 3가지 정도를 고려할 수 있다. 다중성, 방향, 연관관계 주인 이렇게 3가지를 고려하여 연관관계를 맵핑 시켜주어야 한다.

방향

단방향양방향이 존재하며, 실제 DB테이블은 방향이 없이 FK를 가지고 두테이블 모두 조회가 가능하나, 객체는 참조용 필드를 가지고 연관된 객체를 조회하는 식으로 표현하기 때문에 방향이 존재한다.

연관관계 주인

마찬가지로 테이블은 하나의 FK로 두 테이블의 연관관계를 표현하지만 엔티티는 객체의 연관관계를 관리하는 곳이 각각 한개씩 2곳이기 때문에, 둘 중 주한쪽을 정해 외래키를 관리하도록 해야하고 주로, 외래키를 가진 테이블과 매핑한 엔티티가 관리하는게 효율적이라 이를 주인으로 선택하고 주인은 mapped by를 사용하지 않는다.

다중성

1. 다대일 (ManyToOne)

다대일의 양방향은 외래키가 있는 쪽이 연관관계의 주인이고, 항상 서로를 참조해야 한다. 항상 서로 참조하게 하기 위해 편의 메소드를 작성하는 것이 좋고 양쪽에 모두 작성했을때 순환 참조로 인해 무한 루프에 빠지는 것을 주의해야하고 검사하는 로직을 추가해주는 것이 좋다.

2. 일대다 (OneToMany)

일대다의 단방향관계를 맵핑할때는 @JoinColumn을 명시해야 JPA가 조인 테이블전략을 사용하여 맵핑하지 않는다. 또한, 주로 외래키는 다(N)쪽 테이블에 있기 때문에 본인의 객체만 관리하는 것이 아닌 참조하는 객체또한 관리를 해주어야 해서 많은 단점이 존재한다. (ex. Insert 쿼리를 날리때, 참조하고있는 객체도 처리를 해주어야 하기 때문에, update sql을 추가로 실행해야한다.)

일대다 양방향관계를 결국 다대일 양방향과 다르지 않다.

굳이 표현하고자 한다면, 반대쪽에도 다대일 단방향을 추가함으로써 맵핑할 수 있지만 두 곳에서 키를 관리하기 때문에 문제가 발생할 수 있기 때문에 다(N)쪽은 insertable = false, updatable =fasle로 읽기만 가능하게 해야 한다.

3. 일대일 (OneToOne)

이는 양쪽 모두 주인이 될 수 있고 한개만 키를 갖도록 선택해야 한다.

주 테이블에 외래키를 두는 것이 객체 참조와 비슷하게 사용할 수 있고, 주 테이블만 확인해도 대상 테이블과 연관관계가 있는지 확인할 수 있다.

대상테이블에 외래키를 두는 것은 일대일에서 일대다로 변경할 때 테이블 구조를 그대로 유지할 수 있는 장점이 있다.

4. 다대다 (ManyToMany)

RDB는 정규화된 테이블 2개로 다대다를 표현 할 수 없어 중간에 연결 테이블을 추가해서 연결해야 한다. @ManyToMany@JoinTablename/ joinColumns / inverseJoinColumns를 통해 참조하는 객체가 아닌 연결 테이블로 맵핑을 설정 해주어야 한다.

위 방법은 조인하는 과정에 있어 키가 아닌 그 외의 컬럼들은 연결 테이블에 담지 못하는 단점이 존재한다. 그래서 엔티티를 한개 더 만들고 해다 엔티티에서 추가 컬럼과 두개의 엔티티를 @ManyToOne을 통해 맵핑을 시켜주면 된다. 이때 보통 해당 엔티티의 키는 양쪽의 키로 구성된 복합키이기 때문에 IdClassEmbeddedId를 통해 맵핑 해주면 된다. (복합키를 위한 식별자 클래스도 생성하고 이를 지정해주면 된다.)

복합키를 위한 식별자 클래스의 특징

  1. 별도의 식별자 클래스로 구성

  2. Serializable 를 구현해야한다. (implements)

  3. equals와 hashcode 메소드를 구현해야한다.

  4. 기본 생성자가 있어야 한다.

  5. 식별자 클래스는 public

연결 테이블의 주 키는 자동 생성 키를 대리 키로 사용하는 것이 간편하고, 비즈니스에 의존적이지 않고 ORM매핑시 복합 키를 만들지 않아도 되 간단하게 매핑을 할 수 있다.

정리

외래키는 N(다)에 들어있기 때문에 N에서 관리하고 1은 mapped by를 통해 주인이 아님을 명시해주는 것이 좋다.


Reference

자바 ORM 표준 JPA 프로그래밍 책 (김영한 저)

Last updated