연관관계 매핑
ORM은 Object Relation Mapping의 약자로 객체와 관계형 데이터베이스 사이의 간격을 객체(엔티티)에 연관관계를 매핑해서 관계형 데이터베이스와 소통하는 기술이다. 엔티티 매핑은 객체와 데이터베이스 사이의 일대일로 대응되기에 JPA 어노테이션과 디테일 설정을 통해 쉽게 설정할 수 있다. 연관관계 매핑은 객체의 참조와 테이블의 외래 키를 매핑을 의미하는데 JPA에서는 PK가 아닌 PK에 해당하는 엔티티 객체 자체를 참조한다.
연관 관계시에 고려해야할 것
방향성
데이터베이스는 외래 키로 테이블 조인을 하기에 방향성이 없지만, 객체는 참조를 통해 다른 객체에 접근할 수 있어 방향성을 가진다.
단방향 연관관계 : 두 엔티티 사이에 하나의 객체만 다른 객체를 참조
양방향 연관관계 : 두 엔티티가 서로를 단방향으로 참조
서비스에 따라 단방향 연관관계로 끝을 낼지, 양방향 연관관계를 맺을지 선택해야한다.
다중성
데이터베이스를 기준으로 다중성을 결정한다. 기준 엔티티에 따라 다중성이 달라진다.
ManyToOne : N:1
OneToMany: 1:N
OneToOne : 1:1
ManyToMay : N:N
보통은 OneToMany와 ManyToOne의 조합, OneToOne과 OneToOne의 조합을 사용하고 ManyToMany는 개발자가 구현하지 않은 중간 테이블이 생성되어 예상치 못한 쿼리가 나갈 수 있어 중간 테이블을 엔티티로 만들어 1:N, N:1의 조합으로 풀어 사용한다.
연관관계의 주인
양방향 연관관계를 맺게되면 외래키의 제어권한을 부여하는 연관관계의 주인을 정해야한다.
연관관계의 주인은 외래키를 관리(쓰기작업)를 관장하고, 주인이 아닌쪽은 읽기작업만 가능하다.
연관관계의 주인을 정하지 않으면 Member를 추가할 때
1. Member 객체를 생성한 다음
2-1. Team의 members에 추가된 것을 DB에 넣을지
2-2. 생성한 Member 객체에 Team을 설정한 후에 DB에 넣을지
JPA가 데이터베이스에게 알려줄 수 없다.
외래키 필드를 연관관계의 주인으로 정하는게(2-2번 과정) 성능면에서도, 논리적 오류가 날 가능성도 적다.
연관관계 주인 필드는 @JoinColumn을, 주인이 아닌 엔티티의 필드는 다중성 어노테이션에 mappedBy 속성으로 주인을 정한다.
연관관계의 주인은 JPA와 데이터베이스 사이에서 쓰기작업을 편하게 하기위해 지정하는 것일뿐 비즈니스 로직적으로 보면 우열관계는 존재하지 않는다.
연관관계 주인이 아닌쪽의 데이터 관리
연관관계 주인 필드를 기준으로 데이터를 추가하면 DB로 커밋후에는 연관관계 주인이 아닌쪽에서 접근해도 추가된 데이터에 접근이 가능하지만, 커밋되기 전 영속성 컨텍스트를 사용하거나 JPA를 제외한 테스트를 하기 위해서는 연관관계 주인이 아닌쪽에도 데이터를 추가하는 작업이 필요한데 이를 위해 연관관계 편의 메서드를 사용한다.
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Team {
@Id @GeneratedValue
private Long id;
private String name;
@OneToMany(mappedBy = "team")
private List<Member> members = new ArrayList<>();
public Team(String name) {
this.name = name;
}
public void addMember(Member member){ // 연관관계 편의 메서드, Team도 추가된 Member를 리스트에 추가하고, Member도 Team을 설정
member.setTeam(this);
members.add(member);
}
}
연관관계 편의 메서드를 사용하면 영속성 컨텍스트에 올라가있을 때에도 Team의 MemberList를 통해 Member에 접근이 가능해진다. 연관관계 편의 메서드를 위해서는 엔티티 클래스의 필드에 setter를 추가해야하는데 이로 인해 생길 수 있는 변경가능성을 인지하고 사용해야 한다.
양방향 연관관계 매핑의 주의점
서로가 서로를 참조하기 때문에 ToString, JSON 객체로 변환시에 순환참조로 인해 StackOverFlow가 발생할 수 있다.
따라서 엔티티를 그대로 반환하지 않고 순환참조를 끊는 DTO를 사용하거나, @JsonIgnore를 사용할 수 있다.