[JPA] @ManyToMany를 사용하면 안 되는 이유

박재빈·2024년 11월 26일
0

Java의 JPA에서 @ManyToMany를 사용하면 연관관계 매핑에서 다대다 관계를 간단히 표현할 수 있습니다. 하지만 실무에서는 @ManyToMany를 사용하는 경우는 드뭅니다. 대부분 중간 테이블을 명시적으로 정의하는 방식을 선호합니다. 왜 @ManyToMany를 사용을 지양해야하는지에 대해 알아보겠습니다.

1. @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<>();
}

위의 코드는 다대다 연관관계를 간단하고 직관적으로 보여주지만 다음과 같은 문제점이 있습니다.

2. @ManyToMany의 문제점

1. 중간 테이블

@ManyToMany는 중간 테이블을 자동으로 생성하고 관리합니다. 하지만 실무에서는 중간 테이블에 추가 속성을 두어야 할 때가 많습니다.

예를 들어 사용자(User)와 역할(Role)간의 관계에서, 역할을 부여한 날짜(assignedAt)를 저장하고 싶다고 가정을 하면 위에서와 같이 @ManyToMany로 설계한 경우 사용자와 역할 간의 관계만 저장할 수 있고, 역할을 부여한 날짜와 같은 추가 정보를 저장할 방법은 없습니다.

2. 성능

@ManyToMany는 관계를 관리하기 위해 내부적으로 많은 조인 쿼리를 생성합니다. 관계가 복잡해질수록 성능 문제가 발생할 가능성이 높습니다.

3. 대안 : 중간 엔티티를 명시적으로 정의하기

@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; // 추가 속성
}

0개의 댓글