Spring boot & JPA

Spring Boot - 쇼핑몰 프로젝트 05 (연관 관계 매핑 - 1)

로기221 2023. 4. 5. 14:36
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을 조회하면 중간 테이블이 있기 때문에 어떤 쿼리문이 실행될지 예측하기도 쉽지 않음

다대다 매핑의 진행방식 (프로젝트에 추가X)

 


https://rogi221.tistory.com/175

 

Spring Boot - 쇼핑몰 프로젝트 05 (연관 관계 매핑 - 2)

https://rogi221.tistory.com/174 Spring Boot - 쇼핑몰 프로젝트 05 (연관 관계 매핑 - 1) 연관 관계 매핑 - 1 연관 관계 매핑 종류 엔티티들은 대부분 다른 엔티티와 연관 관계를 맺으며, JPA에서는 엔티티에 연

rogi221.tistory.com

 

 

 

 

 

 

728x90
반응형