연관관계와 관계형 데이터 베이스 설계
관계형 데이터베이스에서는 개체 간의 관계
라는 것에 대해 고민하게 된다.
관계형 데이터베이스에서는 일대일(1:1), 일대다(1:N), 다대다(M:N)의 관계를 이용해서 데이터가 서로 간에 어떻게 구성되었는지를 표현한다.
위 관계를 가장 쉽게 확인할 수 있는 방법은 직접 가상의 데이터를 만들어 보는 것이다.
데이터베이스에서 관계를 해석할 때는 항상 PK쪽에서 해석한다.
JPA에서 연관관계 해석하기
JPA를 이용해서 연관관계를 해석할 때는 PK를 기준으로 잡고, 데이터베이스를 모델링하는 방식으로 구성한다.
엔티티 생성하기
|
|
Member 클래스 이메일 주소를 PK로 이용한다. 데이터베이스 설게에서도 member 테이블은 PK만을 가지고 있고, FK를 사용하지 않으므로 별도의 참조가 필요하지 않는다.
|
|
@ManyToOne 어노테이션
JPA에서 가장 많이 사용하고, 꼭 알아야하는 다중성이다.
(OneToMany는 JPA에서 지원하긴 하지만 권장하지 않는 방식이다.)
데이터베이스 구조로 보면 board 테이블과 member 테이블에는 FK를 이용한 참조가 걸려 있게 된다.
JPA에서 관계를 고민할 때는 FK 쪽을 먼저 해석해보면 편리하다.
board와 member의 관게는 N:1(다대일)의 관계가 되므로 JPA에서는 이를 의미하는 @ManyToOne을 적용해야 한다.
@ManyToOne은 데이터베이스상에서 외래키의 관계로 연결된 엔티티 클래스에 설정한다.
일대 다에서 다 쪽에 외래키가 있어야한다.
@ManyToOne과 Eager/Lazy Loading
두개 이상의 엔티티 간의 연관관계를 맺고 나면 데이터베이스 입장에서는 연관관계를 맺고 있기 때문에 조인이 필요하다고 생각한다.
실제로 @ManyToOne의 경우에는 FK 쪽의 엔티티를 가져오때 PK 쪽의 엔티티도 같이 가져온다.
따라서 연관관계에 있는 값을 가져오려면 자동적으로 조인이 된다.
조인이 필요없을 때도 조인이 발생하기 때문에 시스템의 비효율을 야기할 수 있다.
그래서 fetch는 Lay loading을 권장한다.
위 처럼 쿼리 실행결과와 같이 특정한 엔티티를 조회할때 연관관계를 가진 모든 엔티티를 같이 로딩하는 것을 Eager loading
이라고 한다. 이는 즉시 로딩
이라는 말로도 표현하기도 한다.
JPA에서 연관관계를 데이터를 어떻게 가져올 것인가를 fetch(패치)
라고 하는데 연관관계의 어노테이션의 속성으로 fetch 모드를 지정한다.
즉시 로딩은 불필요한 조인까지 처리해야 하는 경우가 많기 때문에 간으하면 사용하지 않고, 그와 반대되는 개념으로 Lazy loading
으로 처리한다. 이는 지연 로딩
이라고 부르기도 한다.
지연로딩을 사용하게 되면 즉시로딩보다는 확실히 효율적으로 값을 가져올 순 있지만, 단점 또한 존재한다.
|
|
위 코드를 지연로딩을 사용해서 수행하면 오류가 발생한다.
board.getWriter는 member 테이블을 로딩해야 하는데 이미 데이터베이스와의 연결이 끝났기 때문에 에러가 발생한다.
이러한 상황으로 보아 지연로딩은 단순하게 하나의 테이블을 이용하는 경우에는 빠른 속도의 처리가 가능하다는 장점이 있지만, 연관관계가 복잡한 경우에는 여러 번의 쿼리가 실행된다는 단점이 있다.
따라서 보편적인 코딩 가이드는 지연로딩을 기본으로 사용하고, 상황에 맞게 필요한 방법을 찾는다
이다.
이때 @Transactional을 사용하면 에러를 해결할 수 있다.
@Transactional은 해당 메서드를 하나의 트랜잭션으로 처리하라는 의미이다.
연관관계에서는 @ToString()을 조심
@ToString()을 사용하게 되면 해당 클래스의 모든 멤버변수를 출력하게 된다. 예를 들어 Board객체의 TosString을 하려면 Member도 호출해야하는 것이다.
따라서 연관관계가 있는 엔티티 클래스의 경우 @ToString()을 할 때는 습관적으로 exclude
속성을 사용하는 것이 좋다.