[뉴스 App] JPA 다대다 연관관계 매핑을 해보자.

김성수·2023년 5월 12일
1

뉴스어플리케이션

목록 보기
5/5

뉴스 어플리케이션에 카테고리 엔티티를 추가해보려 한다.
카테고리 엔티티는 뉴스 엔티티와 다대다 연관관계이다.
카테고리 엔티티와 뉴스 엔티티를 연관관계 매핑 해보려 한다.



매핑 테이블

다대다 연결관계 매핑은 중간에 매핑 테이블이 필요하다.

News 엔티티와 Category 엔티티를 만들고

그 중간에 매핑 테이블 NewsCategory를 만든다.

News와 NewsCategory는 일대다의 관계를 가지고

Category와 NewsCategory 또한 일대다의 관계를 가진다.

그 이유를 예를 들자면

News 테이블의 식별자 1번이 정치, 스포츠, 경제 카테고리에 속한다면

NewsCategory 테이블에는 news_id 컬럼 1번에
스포츠 레코드 하나
정치 레코드 하나
경제 레코드 하나

이렇게 총 세게의 레코드가 생긴다.

그러므로 News와 NewsCategory는 일대다 관계를 가진다.



매핑 테이블이 필요한 이유

중간 매핑 테이블이 필요한 이유는

데이터 정규화 규칙을 준수하기 위해서이다.

데이터 정규화는 데이터베이스 설계 시 데이터 중복을 최소화하고 데이터 일관성을 유지하기 위함이다.

예를 들어 하나의 뉴스는 여러 카테고리에 속할 수 있고,
하나의 카테고리에 여러 뉴스가 들어갈 수 있다.

이는 다대다 관계이다.

만약 중간 매핑 테이블이 없다고 가정해보자.

news 테이블의 id 1번에 정치, 스포츠, 경제 카테고리가 존재한다.

그러면 news 테이블에서 id가 1인 세개의 레코드가 존재하게 된다.

또한 category 테이블에서도 정치, 스포츠, 경제 카테고리에 news_id 1번이 속해야 하므로

세개의 레코드가 존재해야 한다.

이렇게 되는 경우 중복 레코드가 발생하므로 데이터 정규화 규칙에 어긋나고

또한 데이터를 수정해야 하는 경우 중복되는 데이터를 모두 수정해야 하는데, 그 과정에서 실수로 수정을 빼먹게 되면

데이터의 일관성을 유지하지 못하게 되므로 문제가 될 수 있다.

따라서, 다대다 관계에서는 중간 매핑 테이블이 필요하다.



데이터 일관성이란?

하나의 데이터가 여러 곳에서 사용될 때,
데이터가 일관성있는 상태를 유지해야 하는 것을 의미한다.

당연히 정확하게 데이터를 저장해야할 것이고, 외부에서 사용하는 데이터와 서로 일치한 값을 가지고 있어야할 것이다.

데이터가 수정되었을 때 외부에서 참조하고 있는 해당 데이터도 함께 수정되어 일관성이 있어야 한다.



연관관계 주인 설정

  1. 외래키가 위치한 엔티티는 일대다에서 다에 해당한다.

  2. 외래키가 있는 곳에 연관관계 주인으로 설정한다.

1번부터 이유를 알아보자.

왜 외래키가 위치한 엔티티는 일대다에서 다(n)에 해당할까?

만약 기본키를 가지고 있는 엔티티를 다로 설정한다고 가정해보자.

//직원 릴레이션
emp_id name dept_id
1 Alice 1
2 Bob 1
3 Charlie 2

//부서 릴레이션
dept_id dept_name
1 Sales
2 Marketing

직원과 부서 테이블의 관계를 생각해보면 직원이 다(n)
부서가 (1)에 해당한다.

그런대 반대로 부서를 (n) 직원을 1로 설정하게 되면 위처럼

아래와 같이 테이블이 형성될 수 있다.

// 부서가 (n)일 때 릴레이션
dept_id dept_name emp_id
1 Sales 1
1 Sales 2
2 Marketing 3

위처럼 dept_id가 1로 중복되어서 저장되게 되면

외래키 테이블인 직원 테이블에서 부서 테이블의 기본키를 참조하기 때문에

서로 같은 dept_id에서 어떤 것을 참조해야할지 기준이 모호해지고

이는 예상치 못한 결과로 이어질 수 있게 된다.


그러면 이제

2번인 연관관계 주인이 외래키를 관리해야한다를 알아보자.

왜 연관관계 주인이 외래키를 관리해야할까?

그건 JPA의 영속성 컨텍스트 기능 때문이다.

JPA가 외래키 변경을 감지하고(dirty checking) 연관된

다른 엔티티의 외래키 값도 자동으로 변경된다.

예를 들어 직원의 부서 ID를 변경해주고 싶다면 아래와 같이 코드를 입력할 수 있다.

Department department = entityManager.find(Department.class, 1L);
List<Employee> employees = department.getEmployees();
for (Employee employee : employees) {
    employee.setDepartment(anotherDepartment); // 다른 부서로 변경
}

부서 1번인 직원을 모두 조회하여 List로 담아주고

iter 문으로 해당 직원들의 부서를 모두 다른 부서로 변경한다.

영속성 컨텍스트의 변경 감지(dirty checking)로 인해 변경된 값이 자동으로 업데이트 되어 DB에 저장된다.


1번과 2번을 종합해서 정리해보자면

외래키가 있는 엔티티가 일대다에서 다(n)에 해당하게된다.

이는 데이터 중복 방지와 데이터 일관성을 지키기 위함이다.

또한 외래키가 있는 곳에 연관관계 주인을 맺어줘야 하는데 그 이유는

JPA가 외래키를 관리할 수 있도록하기 위함이다.

JPA가 외래키가 있는 곳에 위치함으로써 외래키가 변경되었을 때

자동으로 변경된 값을 업데이트쳐서 DB에 보내준다.

만약 연관관계 주인을 기본키가 있는 곳에 둔다면 외래키가 변경되어도 자동으로 인식하지 못할 것이고,

영속성 컨텍스트에서도 관리하지 못하게 되므로 JPA가 인식하는 트랜잭션에 구멍이 생겨서 예상치 못한 결과가 나올 수도 있고

개발자의 실수로 flush 또는 persist 해주지 않게 되면 업데이트가 되지 않아 문제가될 수도 있다.



Category와 NewsCategory 매핑

Category 엔티티와 NewsCategory는 일대다 관계를 가진다.

//NewsCategory
@Entity
@Getter @Setter
public class NewsCategory {

    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name="news_category_id")
    private Long id;

    @ManyToOne
    @JoinColumn(name="category_id")
    private Category category;
}

위 클래스에서 @ManyToOne과 @JoinColumn에 집중해보자.

    @ManyToOne
    @JoinColumn(name="category_id")
    private Category category;

@ManyToOne은 현재 엔티티가 다(n)에 해당한다는걸 의미한다.
@JoinColumn(name="category_id")은 category_id를 join한다는 의미이자.

이 컬럼이 외래키라는걸 의미한다.


아래는 Category 엔티티이다.

// Category
@Entity
@Getter @Setter
public class Category {

    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "category_id")
    private Long id;

    @OneToMany(mappedBy = "category")
    private List<NewsCategory> newsCategories = new ArrayList<>();
}

@OneToMany(mappedBy = "category")는 1에 해당한다는 것을 의미한다.

또한 (mappedBy = "category")는 내가 지금 category를 매핑하고 있고

나는 의존관계 주인이 아니기 때문에 read 기능만 수행한다는 것을 의미한다.



News, Category, NewsCategory 테이블 생성 확인

위 이미지를 보면 Category 테이블과 News 테이블은 NewsCategory 관련 컬럼이 없다.

하지만 NewsCategory 테이블은 category_id 컬럼과 news_id 컬럼을 가지고 있는 것을 확인할 수 있다.

profile
깊이 있는 소프트웨어 개발자가 되고 싶습니다.

0개의 댓글