Java의 JPA에서 @ManyToMany를 사용하면 연관관계 매핑에서 다대다 관계를 간단히 표현할 수 있습니다. 하지만 실무에서는 @ManyToMany를 사용하는 경우는 드뭅니다. 대부분 중간 테이블을 명시적으로 정의하는 방식을 선호합니다. 왜 @ManyToMany를 사용을 지양해야하는지에 대해 알아보겠습니다.
@ManyToMany는 JPA에서 두 엔티티 간의 다대다 연관관계를 간단하게 표현하기 위한 애노테이션입니다. 예를 들어, 사용자와 역할간의 다대다 관계를 @ManyToMany로 다음과 같이 매핑할 수 있습니다.
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@ManyToMany
@JoinTable(name = "user_roles",
joinColumns = @JoinColumn(name = "user_id"),
inverseJoinColumns = @JoinColumn(name = "role_id"))
private List<Role> roles = new ArrayList<>();
}
@Entity
public class Role {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@ManyToMany(mappedBy = "roles")
private List<User> users = new ArrayList<>();
}
위의 코드는 다대다 연관관계를 간단하고 직관적으로 보여주지만 다음과 같은 문제점이 있습니다.
@ManyToMany는 중간 테이블을 자동으로 생성하고 관리합니다. 하지만 실무에서는 중간 테이블에 추가 속성을 두어야 할 때가 많습니다.
예를 들어 사용자(User)와 역할(Role)간의 관계에서, 역할을 부여한 날짜(assignedAt)를 저장하고 싶다고 가정을 하면 위에서와 같이 @ManyToMany로 설계한 경우 사용자와 역할 간의 관계만 저장할 수 있고, 역할을 부여한 날짜와 같은 추가 정보를 저장할 방법은 없습니다.
@ManyToMany는 관계를 관리하기 위해 내부적으로 많은 조인 쿼리를 생성합니다. 관계가 복잡해질수록 성능 문제가 발생할 가능성이 높습니다.
@ManyToMany를 사용하는 것이 아닌 중간 엔티티를 별도로 정의하는 방식을 많이 사용합니다.
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToMany(mappedBy = "user")
private List<UserRole> userRoles = new ArrayList<>();
}
@Entity
public class Role {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToMany(mappedBy = "role")
private List<UserRole> userRoles = new ArrayList<>();
}
@Entity
public class UserRole {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
@JoinColumn(name = "user_id")
private User user;
@ManyToOne
@JoinColumn(name = "role_id")
private Role role;
private LocalDateTime assignedAt; // 추가 속성
}