본문 바로가기
개발관련

JPA - ManyToMany 관계시 Set과 List의 차이

by 부발자 2020. 8. 11.

JPA 로 개발을 할때, @ManyToMany 관계를 맺는 경우가 있는데,

이때 Set 과 List 의 차이점을 알고 사용하도록 하자.

 

Category - Article 관계가 n:m 이라고 가정을 하자.


@ManyToMany 관계시 List 를 사용하였을 경우

@Entity
public class Category {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private long id;

  private String name;

  @ManyToMany(mappedBy = "categories")
  private List<Article> articles = new ArrayList<>();
  
  public void deleteArticle(Article article) {
    articles.remove(article);
  }
}
@Entity
public class Article {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private long id;

  private String title;

  @ManyToMany
  @JoinTable(name = "article_category",
      joinColumns = { @JoinColumn(name = "article_id") },
      inverseJoinColumns = { @JoinColumn(name = "category_id") })
  private List<Category> categories = new ArrayList<>();

  public void deleteCategory(Category category) {
    this.categories.remove(category);
    category.deleteArticle(this);
  }

  public void addCategory(Category category) {
    this.categories.add(category);
    category.deleteArticle(this);
  }
}

 

 

초기 데이터는 다음과 같이 세팅하였다.

Category 2건과 Article 1건을 INSERT 하고, 매핑 테이블에 관계를 맺었다.

insert into category (id, name) values (1, "1")
insert into category (id, name) values (2, "2")
insert into article (id, title) values (1, "제목")
insert into article_category (article_id, category_id) values (1, 1)
insert into article_category (article_id, category_id) values (1, 2)

 

이제 테스트를 해보자.

 

Article 에 기존에 저장되어 맺고 있던 Category(id=1, name="1") 의 관계를 삭제하고, 새로운 Category(id=3, name="3") 삽입하고, 관계를 맺는 로직이다.

Category newCategory = new Category();
newCategory.setName("3");
categoryRepository.save(newCategory);

Category category = categoryRepository.findById(1L).orElseThrow(EntityNotFoundException::new);
Article article = articleRepository.findById(1L).orElseThrow(EntityNotFoundException::new);

article.deleteCategory(category);
article.addCategory(newCategory);

 

쿼리가 다음과 같이 찍히는것을 볼수 있다.

article_category 테이블에 article_id 에 해당하는 모든 관계를 삭제하고,

기존에 남아있던 Category(id=2, name="2") 와 신규로 생성된 Category(id=3, name="3") 의 관계를 삽입하고 있다.

insert into category (id, name) values (null, ?)

...(select 생략)

delete from article_category where article_id=?
insert into article_category (article_id, category_id) values (?, ?)
insert into article_category (article_id, category_id) values (?, ?)

 

이렇게 한다면, 성능상의 문제와 효율적이지 못한 문제가 발생한다.

만약 100개의 Category 와 관계를 맺은 상태에서 저 위의 로직으로 동작을 한다면, 어떻게 될것인가?

 

 


@ManyToMany 관계시 Set 을 사용하였을 경우

@Entity
public class Category {

  ... 생략

  @ManyToMany(mappedBy = "categories")
  private Set<Article> articles = new HashSet<>();
}
@Entity
public class Article {

  ... 생략
  
  @ManyToMany
  @JoinTable(name = "article_category",
      joinColumns = { @JoinColumn(name = "article_id") },
      inverseJoinColumns = { @JoinColumn(name = "category_id") })
  private Set<Category> categories = new HashSet<>();
}

 

쿼리는 다음과 같이 찍히는것을 볼수 있다.

List 와 다르게, 삭제 하고자 하는 Category(id=1, name="1") 를 1건의 관계만 삭제하고, 새로운 Category(id=3, name="3") 1건의 관계만 삽입하고 있다.

insert into category (id, name) values (null, ?)

... (select 생략)

delete from article_category where article_id=? and category_id=?
insert into article_category (article_id, category_id) values (?, ?)

 


JPA 에서 ManyToMany 의 관계일때 Set 과 List의 차이점을 알고 사용해야 하며,

가능하다면 Set 으로 하는것이 좀더 옳은 방법인 것 같다.

반응형