JPA에서 다대다 연관관계(Many-to-Many Relationship)를 직접 사용하는 것은 권장되지 않는 경우가 많습니다. 그 이유는 여러 가지 제약과 관리상의 문제 때문입니다. 아래에 그 구체적인 이유를 설명합니다.
다대다 관계는 내부적으로 조인 테이블을 생성하여 두 엔티티 간의 매핑을 관리합니다.
하지만, JPA의 기본 다대다 매핑에서는 조인 테이블을 독립적으로 제어할 수 없습니다.
문제:
조인 테이블에 추가적인 속성(예: 생성일, 수정일 등)을 저장할 수 없습니다.
조인 테이블을 직접 다룰 방법이 없어 관리가 어려워집니다.
예시:
@Entity
public class Member {
@ManyToMany
@JoinTable(name = "member_team")
private List<Team> teams;
}
JPA는 member_team이라는 중간 테이블을 자동 생성하지만, 이 테이블에 추가적인 정보를 넣을 수 없습니다.
다대다 관계는 실무에서 비즈니스 요구사항이 복잡해질수록 확장성이 떨어집니다. 조인 테이블에 비즈니스 속성(예: "회원이 팀에 가입한 날짜")을 추가해야 할 때, 기본 다대다 매핑으로는 불가능합니다.
해결법:
조인 테이블을 독립된 엔티티로 만들어 다대일 관계로 풀어내는 것이 좋습니다.
예시 (권장 방식):
@Entity
public class MemberTeam {
@Id @GeneratedValue
private Long id;
@ManyToOne
private Member member;
@ManyToOne
private Team team;
private LocalDate joinDate; // 추가 속성
}
이렇게 하면 조인 테이블에 속성을 추가하거나, 조인 테이블을 명시적으로 다룰 수 있습니다.
다대다 관계에서는 JPA가 조인 쿼리를 생성합니다. 데이터가 많아질수록 성능 저하가 발생할 수 있습니다.
다대다 관계에서는 중간 테이블의 데이터 무결성을 보장하기 어렵습니다.
특히 다음과 같은 경우 문제가 발생할 수 있습니다:
다대다 관계는 객체 지향 설계 관점에서 부자연스러운 경우가 많습니다.
실제 비즈니스 도메인에서는 대부분 중간 엔티티가 존재하며, 이를 모델링하지 않으면 객체 간의 관계를 제대로 표현할 수 없습니다.
중간 엔티티를 사용하여 다대다 관계를 풀어서 표현하는 것이 일반적입니다.
이렇게 하면 비즈니스 로직을 쉽게 확장하고, JPA의 장점을 극대화할 수 있습니다.
@Entity
public class Member {
@OneToMany(mappedBy = "member")
private List<MemberTeam> memberTeams;
}
@Entity
public class Team {
@OneToMany(mappedBy = "team")
private List<MemberTeam> memberTeams;
}
@Entity
public class MemberTeam {
@ManyToOne
private Member member;
@ManyToOne
private Team team;
private LocalDate joinDate; // 조인 테이블 속성
private String role; // 추가 속성
}
외래 키를 설정하는 방식은 관계의 주인(Owner)과 연관이 있습니다.
다대일 관계에서 외래 키는 항상 자식 엔티티에 존재합니다.
다대일 관계는 JPA에서 가장 일반적이고 단순한 매핑입니다.
@Entity
public class Member {
@ManyToOne
@JoinColumn(name = "team_id") // 외래 키 지정
private Team team;
}
일대다 관계는 외래 키가 자식 엔티티에 있어야 하므로, 관계의 주인은 Many 쪽입니다.
일반적으로 일대다 매핑에서 외래 키는 자식 엔티티에 정의되며, 아래와 같이 설정됩니다:
단방향 일대다 관계는 잘 사용되지 않습니다.
이유: 외래 키가 Many 쪽에 위치하는데, JPA는 기본적으로 One 쪽에서 관리하려 하므로 비효율적입니다.
@Entity
public class Team {
@OneToMany
@JoinColumn(name = "team_id") // 외래 키가 Member 테이블에 생성됨
private List<Member> members;
}
양방향 관계에서는 일반적으로 다대일 쪽을 관계의 주인(Owner)으로 설정하고, 일대다 쪽은 매핑만 담당합니다.
@Entity
public class Team {
@OneToMany(mappedBy = "team") // 관계의 주인은 Member
private List<Member> members;
}
@Entity
public class Member {
@ManyToOne
@JoinColumn(name = "team_id") // 외래 키 지정
private Team team;
}
JPA에서 기본 설정(Default Mapping)을 사용할 경우, 명시적으로 외래 키를 지정하지 않아도 됩니다.
JPA는 아래와 같은 규칙을 기반으로 외래 키를 자동 생성합니다:
예:
@Entity
public class Member {
@ManyToOne
private Team team; // team_id 외래 키가 자동 생성
}
위 코드에서 @JoinColumn을 생략하면 JPA는 기본적으로 team_id라는 외래 키를 생성합니다.