[스프링 부트 쇼핑몰 프로젝트 with JPA] 5장 정리
엔티티들은 대부분 다른 엔티티와 연관 관계를 맺고 있으며, JPA에서는 이를 매핑해 필요할 때 해당 엔티티와 연관된 엔티티를 사용하여 객체지향적으로 프로그래밍할 수 있도록 도와준다. 연관 매핑의 종류로는 일대일, 일대다, 다대일, 다대다가 있으며, 이는 단방향 또는 양방향으로 설정할 수 있다. 영속성 전이는 엔티티의 상태를 변경할 때 해당 엔티티와 연관된 엔티티의 상태 변화를 전파시키는 옵션이다. 일대일, 다대일로 매핑할 경우 기본 전략인 즉시 로딩을 통해 엔티티를 함께 가지고 오지만, 실제 비즈니스의 경우 매핑되는 엔티티의 개수가 많고, 이 경우 개발자는 쿼리가 어떻게 실행될지 예측할 수 없으므로 지연 로딩 방식을 사용해야 한다. 마지막으로, Auditing 기능을 이용하면 엔티티가 저장 또는 수정될 때 자동으로 등록일, 수정일, 등록자, 수정자를 입력해준다.
Apr 03, 2024
🌼연관 관계 매핑 종류
💡핵심 키워드
- 엔티티들은 대부분 다른 엔티티와 연관 관계를 맺고 있다.
- JPA에서는 엔티티에 연관 관계를 매핑해두고 필요할 때 해당 엔티티와 연관된 엔티티를 사용하여 좀 더 객체지향적으로 프로그래밍할 수 있도록 도와준다.
- 연관 매핑의 종류
- 일대일(1:1) :
@OneToOne
- 일대다(1:N) :
@OneToMany
- 다대일(N:1) :
@ManyToOne
- 다대다(N:M) :
@ManyToMany
- 엔티티를 매칭할 때의 방향성
- 단방향
- 양방향
일대일 단방향 매핑하기
- 회원 한 명당 장바구니는 하나가 존재한다.
- 장바구니, 회원 관계
cart |
cart_id (PK) |
member_id (FK) |
member |
member_id (PK) |
name |
email |
password |
address |
role |
@JoinColumn
어노테이션을 이용해 매핑할 외래키를 지정한다.name
속성에는 매핑할 외래키의 이름을 설정한다.name
속성을 명시하지 않으면 JPA가 알아서 ID를 찾지만 컬럼명이 원하는대로 생성되지 않을 수 있다.
- JPA는 영속성 컨텍스트에 데이터를 저장 후 트랜잭션이 끝날 때
flush()
를 호출하여 데이터베이스에 반영한다.
- JPA는 영속성 컨텍스트로부터 엔티티를 조회 후 영속성 컨텍스트에 엔티티가 없을 경우 데이터베이스를 조회하므로, 실제 데이터베이스에서 장바구니 엔티티를 가지고 올 때 회원 엔티티도 같이 가지고오는지 보기 위해선 영속성 컨텍스트를
clear()
해야한다.
- 엔티티를 조회할 때 해당 엔티티와 매핑된 엔티티도 한 번에 조회하는 것을
즉시 로딩
이라고 한다. - 일대일, 다대일로 매핑할 경우 즉시 로딩을 기본 Fetch 전략으로 설정한다.
다대일 단방향 매핑하기
- 하나의 장바구니에는 여러 개의 상품이 들어갈 수 있고, 같은 상품을 여러 개 주문할 수도 있다.
- 장바구니, 장바구니 상품, 상품 관계
cart |
cart_id (PK) |
member_id (FK) |
cart_item |
cart_item_id (PK) |
cart_id (FK) |
item_if (FK) |
count |
item |
item_id (PK) |
item_nm |
price |
stock_number |
item_detail |
item_sell_status |
다대일/일대다 양방향 매핑하기
- 양방향 매핑이란 단방향 매핑이 2개 있는 것과 유사하다.
- 한 명의 회원은 여러 번 주문을 할 수 있으므로 주문 엔티티 기준에서 다대일 단방향 매핑을 한다.
- 회원, 주문 관계
member |
member_id (PK) |
name |
email |
password |
address |
role |
orders |
order_id (PK) |
member_id (FK) |
order_date |
order_status |
- 하나의 상품은 여러 주문 상품으로 들어갈 수 있으므로 주문 상품 기준으로 다대일 단방향 매핑을 설정한다.
- 한 번의 주문에 여러 개의 상품을 주문할 수 있으므로 주문 상품 엔티티와 주문 엔티티를 다대일 단방향 매핑을 먼저 설정한다.
- 주문, 주문 상품 관계
orders |
order_id (PK) |
member_id (FK) |
order_date |
order_status |
order_item |
order_item_id (PK) |
order_id (FK) |
item_id (FK) |
order_price |
count |
- 엔티티는 테이블과 달리 양방향 연관 관계로 설정되면 객체의 참조는 둘인데 외래키는 하나이므로 둘 중 누가 외래키를 관리할지를 정해야 한다.
- 연관 관계의 주인은 외래키가 있는 곳으로 설정
- 연관 관계의 주인이 외래키를 외래키를 관리(등록, 수정, 삭제)
- 주인이 아닌 쪽은 연관 관계 매핑 시
mappedBy
속성의 값으로 연관 관계의 주인을 설정 - 주인이 아닌 쪽은 읽기만 가능
다대다 매핑하기
- 다대다 매핑은 실무에서는 사용라지 않는 매핑 관계이다.
- 다대다 매핑을 사용하지 않는 이유는 연결 테이블에는 컬럼을 추가할 수 없기 때문이다.
- 연결 테이블에는 조인 컬럼뿐 아니라 추가 컬럼들이 필요한 경우가 많다.
- 또한 엔티티를 조회할 때
member
엔티티에서item
을 조회하면 중간 테이블이 있기 때문에 어떤 쿼리문이 실행될지 예측하기도 쉽지 않다.
- 관계형 데이터베이스는 정규화된 테이블 2개로 다대다를 표현할 수 없다.
- 따라서 연결 테이블을 생성해서 다대다 관계를 일대다, 다대일 관계로 풀어낸다.
- 연결 테이블을 이용한 회원과 상품의 일대다, 다대일 관계
member |
member_id (PK) |
name |
email |
password |
address |
role |
member_item |
member_id |
item_id |
item |
item_id (PK) |
item_nm |
price |
stock_number |
item_detail |
item_sell_status |
- 객체는 테이블과 다르게 컬렉션을 사용해서 다대다 관계를 표현할 수 있다.
member
엔티티는item
을 리스트 형태로 가질 수 있으며,item
엔티티도member
를 리스트로 가질 수 있다.
- 회원, 상품 엔티티 다대다 관계
member |
member_id (PK) |
name |
email |
password |
address |
role |
member_item |
member_id |
item_id |
item |
item_id (PK) |
item_nm |
price |
stock_number |
item_detail |
item_sell_status |
- 다대다 관계의 경우
@ManyToMany
어노테이션을 사용해서 다대다 매핑이 가능하다.
🌼영속성 전이
💡핵심 키워드
영속성 전이란?
- 영속성 전이란 엔티티의 상태를 변경할 때 해당 엔티티와 연관된 엔티티의 상태 변화를 전파시키는 옵션이다.
- 이때 부모는
One
에 해당하고 자식은Many
에 해당한다. - 예를 들어
Order
엔티티가 삭제되었을 때 해당 엔티티와 연관되어 있는OrderItem
엔티티가 함께 삭제되거나,Order
엔티티를 저장할 때Order
엔티티에 담겨있던OrderItem
엔티티를 한꺼번에 저장할 수 있다.
CASCADE 종류 | 설명 |
PERSIST | 부모 엔티티가 영속화될 때 자식 엔티티도 영속화 |
MERGE | 부모 엔티티가 병합될 때 자식 엔티티도 병합 |
REMOVE | 부모 엔티티가 삭제될 때 연관된 자식 엔티티도 삭제 |
REFRESH | 부모 엔티티가 refresh되면 연관된 자식 엔티티도 refresh |
DETACH | 부모 엔티티가 detach되면 연관된 자식 엔티티도 detach 상태로 변경 |
ALL | 부모 엔티티의 영속성 상태 변화를 자식 엔티티에 모두 전이 |
- 영속성 전이 옵션은 단일 엔티티에 완전히 종속적이고 부모 엔티티와 자식 엔티티의 라이프 사이클이 유사할 때
cascade
옵션을 활용해야 한다.
고아 객체 제거하기
- 부모 엔티티와 연관 관계가 끊어진 자식 엔티티를 고아 객체라고 한다.
- 고아 객체 제거 기능은 참조하는 곳이 하나일 때만 사용해야 한다.
@OneToOne
,@OneToMany
어노테이션에서orphanRemoval = true
옵션으로 사용하면 된다.
Cascade
옵션 중REMOVE
옵션과 헷갈릴 수 있다.Cascade REMOVE
옵션은 부모 엔티티가 삭제될 때 연관된 자식 엔티티도 함께 삭제되는 것이다.
🌼지연 로딩
💡핵심 키워드
- 일대일, 다대일로 매핑할 경우 기본 전략인 즉시 로딩을 통해 엔티티를 함께 가지고 온다.
- 이 경우 다대일로 매핑된 엔티티가 있을 경우 작성중인 비즈니스 로직에서 사용하지 않을 데이터도 한꺼번에 들고 온다.
- 실제 비즈니스의 경우 매핑되는 엔티티의 개수는 훨씬 많고, 이 경우 개발자는 쿼리가 어떻게 실행될지 예측할 수 없다.
- 따라서 즉시 로딩을 사용하는 대신에 지연 로딩 방식을 사용해야 한다.
FetchType.LAZY
옵션을 통해 설정할 수 있다.
- 지연 로딩을 실행할 경우 실제 엔티티 대신에 프록시 객체가 넘어온다.
- 프록시 객체는 실제로 사용되기 전까지 데이터 로딩을 하지 않고, 실제 사용 시점에 조회 쿼리문이 실행된다.
🌼Auditing을 이용한 엔티티 공통 속성 공통화
💡핵심 키워드
- 실제 서비스를 운영할 때는 보통 등록시간과 수정시간, 등록자, 수정자를 테이블에 넣어 놓고 활용을 한다.
- 데이터가 생성되거나 수정될 때 시간을 기록해주고, 어떤 사용자가 등록을 했는지 아이디를 남긴다.
- 이 컬럼들은 버그가 있거나 문의가 들어왔을 때 활용이 가능하다.
- 데이터를 대용량으로 업데이트했는데, 다시 업데이트를 해야 할 경우 변경된 대상을 찾을 때 활용할 수도 있다.
Spring Data Jpa
에서는Auditing
기능을 제공하여 엔티티가 저장 또는 수정될 때 자동으로 등록일, 수정일, 등록자, 수정자를 입력해준다.- 이런 공통 멤버 변수들을 추상 클래스로 만들고, 해당 추상 클래스를 상속받는 형태로 엔티티를 리팩토링 할 수 있다.
Auditing
을 적용하기 위해서는@EntityListeners
어노테이션을 추가한다.
- 스프링 시큐리티에서 제공하는 어노테이션으로
@WithMockUser
를 사용하면 지정한 사용자가 로그인한 상태라고 가정하고 테스트를 진행할 수 있다.
🏁결론
해당 내용을 정리하면서 연관 관계 매핑의 종류와 엔티티 연관 관계 매핑을 설정하는 방법, 매핑된 엔티티 조회 시 즉시 로딩과 지연 로딩의 차이점을 이해할 수 있었다.
Share article