728x90
반응형
연관 관계 매핑 - 1
연관 관계 매핑 종류
- 엔티티들은 대부분 다른 엔티티와 연관 관계를 맺으며, JPA에서는 엔티티에 연관 관계를 매핑해두고 필요할 때 해당 엔티티와 연관된 엔티티를 사용하여 좀 더 객체지향적으로 프로그래밍할 수 있도록 도와줌
- 연관 관계 매핑 종류
- 일대일(1:1): @OneToOne
- 일대다(1:N): @OneToMany
- 다대일(N:1): @ManyToOne
- 다대다(N:M): @ManyToMany
- 연관 관계 매핑 방향
- 단방향
- 양방향
연관 관계 매핑 종류 : 일대일 단방향 매핑
- 회원들은 각자 자신의 장바구니를 하나 갖고 있으며 장바구니 입장에서 보아도 자신과 매핑되는 한 명의 회원을 갖는 일대일 매핑 구조
장바구니 엔티티 설계하기
- 장바구니 엔티티는 현재 회원 엔티티에 대한 정보를 알고 있음
- 회원 엔티티에는 장바구니(Cart) 엔티티와 관련된 소스가 전혀 없다는 것을 확인 가능.
- 즉, 장바구니 엔티티가 일방적으로 회원 엔티티
- 를 참조하고 있는 일대일 단방향 매핑입니다.
com.shop.entity.Cart.java
// Cart.java
package com.shop.entity;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import javax.persistence.*;
@Entity
@Table(name = "cart")
@Getter
@Setter
@ToString
public class Cart {
@Id
@Column(name ="cart_id")
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@OneToOne
@JoinColumn(name="member_id")
private Member member;
}
- 애플리케이션을 실행시 콘솔창에 cart 테이블이 생성 및 외래키가 추가되는 쿼리문이 실행되는 것을 볼 수 있음
장바구니 엔티티 조회
장바구니 엔티티 조회 테스트하기(즉시 로딩)
com.shop.repository.CartRepository.java
// CartRepository.java
package com.shop.repository;
import com.shop.entity.Cart;
import org.springframework.data.jpa.repository.JpaRepository;
public interface CartRepository extends JpaRepository<Cart, Long> {
}
com.shop.entity.CartTest.java
// CartTest.java
package com.shop.entity;
import com.shop.dto.MemberFormDto;
import com.shop.repository.CartRepository;
import com.shop.repository.MemberRepository;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.test.context.TestPropertySource;
import javax.persistence.EntityManager;
import javax.persistence.EntityNotFoundException;
import javax.persistence.PersistenceContext;
import javax.transaction.Transactional;
import static org.junit.jupiter.api.Assertions.assertEquals;
@SpringBootTest
@Transactional
@TestPropertySource(locations="classpath:application-test.properties")
public class CartTest {
@Autowired
CartRepository cartRepository;
@Autowired
MemberRepository memberRepository;
@Autowired
PasswordEncoder passwordEncoder;
@PersistenceContext
EntityManager em;
public Member createMember() {
MemberFormDto memberFormDto = new MemberFormDto();
memberFormDto.setEmail("test@email.com");
memberFormDto.setName("홍길동");
memberFormDto.setAddress("서울시 마포구 합정동");
memberFormDto.setPassword("1234");
return Member.createMember(memberFormDto, passwordEncoder);
}
@Test
@DisplayName("장바구니 회원 엔티티 매핑 조회 테스트")
public void findCartAndMemberTest(){
Member member = createMember();
memberRepository.save(member);
Cart cart = new Cart();
cart.setMember(member);
cartRepository.save(cart);
em.flush();
em.clear();
Cart savedCart = cartRepository.findById(cart.getId())
.orElseThrow(EntityNotFoundException::new);
assertEquals(savedCart.getMember().getId(), member.getId());
}
}
- cart테이블과 member 테이블을 조인해서 가져오는 쿼리문 실행
- cart 엔티티를 조회하면서 member 엔티티도 동시에 가져옴
- 즉시 로딩 : 엔티티를 조회할 때 해당 엔티티와 매핑된 엔티티도 한 번에 조회
- 일대일(@OneToOne), 다대일(@ManyToOne)로 매핑할 경우 즉시 로딩을 기본 Fetch 전략으로 설정
연관 관계 매핑 종류 : 다대일 단방향 매핑
- 장바구니에는 고객이 관심이 있거나 나중에 사려는 상품들을 담아둠
- 하나의 장바구니에는 여러 개의 상품들이 들어갈 수 있음
- 하나의 상품은 여러 장바구니에 장바구니 상품으로 들어갈 수 있음
장바구니 아이템 엔티티 설계하기
com.shop.entity.CartItem.java
// CartItem.java
package com.shop.entity;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;
@Entity
@Getter
@Setter
@Table(name="cart_item")
public class CartItem {
@Id
@GeneratedValue
@Column(name="cart_item_id")
private Long id;
@ManyToOne
@JoinColumn(name="cart_id")
private Cart cart;
@ManyToOne
@JoinColumn(name="item_id")
private Item item;
private int count;
}
연관 관계 매핑 종류 : 다대일/일대다 양방향 매핑
- 양방향 매핑이란 단방향 매핑이 2개 있는 구조
- 현재까지는 장바구니 상품 엔티티가(CartItem) 장바구니를(Cart) 참조하는 단방향 매핑.
- 장바구니 엔티티에 장바구니 상품 엔티티를 일대다 관계 매핑을 추가하여 양방향 매핑으로 변경가능
- 주문과 주문 상품의 양방향 매핑 예제
- 주문 상태 Enum 생성
주문 도메인 엔티티 설계하기
com.shop.constatnt.OrderStatus.java
// OrderStatus.java
package com.shop.constant;
public enum OrderStatus {
ORDER, CANCEL
}
com.shop.entity.Order.java
// Order.java
package com.shop.entity;
import com.shop.constant.OrderStatus;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;
import java.time.LocalDateTime;
@Entity
@Table(name="orders")
@Getter
@Setter
public class Order {
@Id
@GeneratedValue
@Column(name="order_id")
private Long id;
@ManyToOne
@JoinColumn(name="member_id")
private Member member;
private LocalDateTime orderDate; // 주문일
@Enumerated(EnumType.STRING)
private OrderStatus orderStatus; // 주문상태
private LocalDateTime regTime;
private LocalDateTime updateTime;
}
com.shop.entity.OrderItem.java
// OrderItem.java
package com.shop.entity;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;
import java.time.LocalDateTime;
@Entity
@Getter
@Setter
public class OrderItem {
@Id
@GeneratedValue
@Column(name="order_item_id")
private Long id;
@ManyToOne
@JoinColumn(name="order_id")
private Item item;
@ManyToOne
@JoinColumn(name="order_id")
private Order order;
private int orderPrice; // 주문가격
private int count; // 수량
private LocalDateTime regTime;
private LocalDateTime updateTime;
}
- 다대일과 일대다는 반대 관계
- 주문 상품 엔티티 기준에서 다대일 매핑이었으므로 주문 엔티티 기준에서는 주문 상품 엔티티와 일대다 관계로 매핑
- 양방향 매핑에서는 ‘연관 관계 주인’을 설정해야 한다는 점이 중요
- ORDERS와 ORDER_ITEM 테이블을 ORDER_ID를 외래키로 조인하면 주문에 속한 상품이 어떤상품들이 있는지 알 수 있고, 주문 상품은 어떤 주문에 속하는지를 알 수 있음
- 즉, 테이블은 외래키 하나로 양방향 조회가 가능
- 엔티티는 테이블과 다릅니다. 엔티티를 양방향 연관 관계로 설정하면 객체의 참조는 둘인데 외래키는 하나이므로 둘 중 누가 외래키를 관리할지를 정해야함.
- 연관 관계의 주인은 외래키가 있는 곳으로 설정
- 연관 관계의 주인이 외래키를 관리(등록, 수정, 삭제)
- 주인이 아닌 쪽은 연관 관계 매핑 시m appedBy 속성의 값으로 연관 관계의 주인을 설정
- 주인이 아닌 쪽은 읽기만 가능
- 외래키(order_id)가 order_item 테이블에 있으므로 연관 관계의 주인은 OrderItem 엔티티
- Order 엔티티가 주인이 아니므로 “mappedBy” 속성으로 연관 관계의 주인을 설정
- 속성의 값으로 “order”를 적어준 이유는 OrderItem에 있는 Order에 의해 관리된다는 의미로 해석.
- 즉, 연관 관계의 주인의 필드인 order를 mappedBy의 값으로 세팅
com.shop.entity.Order.java
// Order.java
package com.shop.entity;
import com.shop.constant.OrderStatus;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
@Entity
@Table(name="orders")
@Getter
@Setter
public class Order {
... 생략 ...
private LocalDateTime orderDate; // 주문일
@Enumerated(EnumType.STRING)
private OrderStatus orderStatus; // 주문상태
@OneToMany(mappedBy = "order") << 추가
private List<OrderItem> orderItems = new ArrayList<>(); << 추가
private LocalDateTime regTime;
private LocalDateTime updateTime;
}
연관 관계 매핑 종류 : 다대다 매핑
- 실무에서는 사용하지 않는 매핑
- 관계형 데이터베이스는 정규화된 테이블 2개로 다대다를 표현할 수 없음
- 연결 테이블을 생성해서 다대다관계를 일대다, 다대일 관계로 풀어냄
- 객체는 테이블과 다르게 컬렉션을 사용해서 다대다 관계를 표현할 수 있음
- item을 리스트 형태로 가질 수 있으며, item 엔티티도 member를 리스트로 가질 수 있음
- @ManyToMany 어노테이션을 사용한다면 아래와 같이 사용 가능
- 다대다 매핑을 사용하지 않는 이유는 연결 테이블에는 컬럼을 추가할 수 없음
- 연결 테이블에는 조인 컬럼뿐 아니라 추가 컬럼들이 필요한 경우가 많음
- 엔티티를 조회할 때 member 엔티티에서 item을 조회하면 중간 테이블이 있기 때문에 어떤 쿼리문이 실행될지 예측하기도 쉽지 않음
https://rogi221.tistory.com/175
Spring Boot - 쇼핑몰 프로젝트 05 (연관 관계 매핑 - 2)
https://rogi221.tistory.com/174 Spring Boot - 쇼핑몰 프로젝트 05 (연관 관계 매핑 - 1) 연관 관계 매핑 - 1 연관 관계 매핑 종류 엔티티들은 대부분 다른 엔티티와 연관 관계를 맺으며, JPA에서는 엔티티에 연
rogi221.tistory.com
728x90
반응형
'Spring boot & JPA' 카테고리의 다른 글
Spring Boot - 쇼핑몰 프로젝트 05 (연관 관계 매핑 - 3) (0) | 2023.04.05 |
---|---|
Spring Boot - 쇼핑몰 프로젝트 05 (연관 관계 매핑 - 2) (0) | 2023.04.05 |
Spring Boot - 쇼핑몰 프로젝트 04 (스프링 시큐리티를 이용한 회원 가입 및 로그인 - 2) (0) | 2023.04.05 |
Spring Boot - 쇼핑몰 프로젝트 04 (스프링 시큐리티를 이용한 회원 가입 및 로그인 - 1) (0) | 2023.04.04 |
Spring Boot - 쇼핑몰 프로젝트 03 (Thymeleaf 학습하기 - 2) (0) | 2023.04.04 |