스프링 부트와 JPA 활용1 - 주문 도메인 개발 2

JOY·2022년 4월 13일
0
post-thumbnail

📌 스프링 부트와 JPA 활용1 - 주문 도메인 개발 2

인프런 - 스프링 부트와 JPA 활용1 by 김영한 을 기반으로 작성된 글입니다.
실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발


주문 도메인 개발 목차

1. 주문, 주문상품 엔티티 개발

2. 주문 리포지토리 개발

3. 주문 서비스 개발

4. 주문 기능 테스트

5. 주문 검색 기능 개발


1) 주문 리포지토리 (OrderRepository.java)

💻 코드

package jpabook.jpashop.repository;

import jpabook.jpashop.domain.Order;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;

import javax.persistence.EntityManager;
import java.util.List;

@Repository
@RequiredArgsConstructor
public class OrderRepository {

    private final EntityManager em;

    public void save(Order order){
        em.persist(order);
    }

    public Order findOne(Long id) {
        return em.find(Order.class, id);
    }

}

📍 설명

1) 기술

  • @Repository : 스프링 빈으로 등록 (Component 스캔 대상)
  • @RequiredArgsConstructor : final 필드만 생성자 생성

1) 기능

  • save() : 주문 엔티티 저장
  • findOne() : 주문 엔티티 검색

이후 추가할 기능

  • findAll() : 주문 검색 기능

2) 주문 서비스 (OrderService.java)

💻 코드 1

package jpabook.jpashop.service;

import (생략)

@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class OrderService {

    private final OrderRepository orderRepository;
    private final MemberRepository memberRepository;
    private final ItemRepository itemRepository;

    /**
     * 주문
     */
    public Long order(Long memberId, Long itemId, int count) {

        //엔티티 조회
        Member member = memberRepository.findOne(memberId);
        Item item = itemRepository.findOne(itemId);

        //배송 정보 생성
        Delivery delivery = new Delivery();
        delivery.setAddress(member.getAddress());

        //주문 상품 생성 
        //예제를 단순히 하기 위해 상품 하나만 선택하도록 설정
        OrderItem orderItem = OrderItem.createOrderItem(item, item.getPrice(), count);
        

📍 설명 1

  • 주문 상품 생성
OrderItem orderItem = OrderItem.createOrderItem(item, item.getPrice(), count);

위 코드를 아래 코드 처럼 개발이 가능하다.

OrderItem orderItem = new OrderItem();
orderItem.setItem(item);
orderItem.setOrderPrice(item.getPrice());
orderItem.setCount(count);
        

그러나 생성로직을 변경할 때 setter를 남발 하다보면
다양한 곳에서 값을 변경할 수 있어 객체의 일관성이 떨어진다.
엔티티에서 setter 사용 대신 생성자를 통해 파라미터를 넘기는게 좋다
💡 그래서 OrderItem.java 에 protected 생성자를 생성

OrderItem.java에 코드 추가

protected OrderItem() {}

protected 생성자를 일일히 생성하는 것 보다
파라미터가 없는 기본 생성자를 자동 생성 해주는
Lombok 어노테이션 @NoArgsConstructor 을 사용하는 것이 편리하다

@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class OrderItem {
	(생략) 
}

@NoArgsConstructor(access = AccessLevel.PROTECTED)
엔티티 클래스 위에 선언하여 간략하게 protected 생성자를 생성할 수 있다

💻 코드 2

        //주문 생성
        Order order = Order.createOrder(member, delivery, orderItem);

        //주문 저장
        orderRepository.save(order);

        return order.getId();
    }

📍 설명 2

  • 주문 생성
  • 주문 저장

delivery와 orderItem 을 따로 생성 / persist 하지 않고
orderRepository.save(order); 의 코드로 바로 save() 해준 이유는 무엇일까 ❓
바로 Order.java의 orderItems 의 cascade 때문이다

order.java

@OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
private List<OrderItem> orderItems = new ArrayList<>();

@OneToOne(fetch = LAZY, cascade = CascadeType.ALL )
@JoinColumn(name="delivery_id")
private Delivery delivery;

cascade = CascadeType.ALL 을 설정해주었기 때문에
orderItem, delivery 둘 다 컬렉션에 있는 모든 엔티티를 자동으로 persist 해준다

cascade (영속성 전이)

부모 엔티티가 영속화될때, 자식 엔티티도 같이 영속화되고
부모 엔티티가 삭제 될때, 자식 엔티티도 삭제되는 등
부모의 영속성 상태가 전이되는 것을 이야기한다
출처: https://coding-start.tistory.com/159 [코딩스타트]

  • cascade의 범위
    : 참조하는 주인이 private 일때 사용
    • delivery와 orderItem을 order만 참조해서 쓰는 경우 사용 O
      👉 조건이 모두 충족되었기 때문에 cascade 사용
    • ex) 다른 엔티티에서 참조해서 사용한다면 cascade 사용 X
      👉 별도의 Repository 생성해서 persist 해주는 것이 좋다

💻 코드 3

    /**
     * 주문 취소
     */
    @Transactional
    public void cancelOrder(Long orderId) {
        //주문 엔티티 조회
        Order order = orderRepository.findOne(orderId);

        //주문 취소
       order.cancel();
    }

📍 설명 3

  • 주문 취소
  1. SQL을 직접 다룰 때, 서비스 계층에서 비즈니스 로직을 쓸 수 밖에 없다
  2. JPA를 활용할 때 Entity 안에 데이터만 변경 한다
    👉 JPA가 바뀐 변경 포인트들을 더티 체킹

더티 체킹이란? (변경 내역 감지)

Transaction 안에 Entity 변경 발생시
변경 내용을 자동으로 DB에 반영하고 Transaction을 commit하는 JPA 특징

📍 정리

  • 도메인 모델 패턴
    엔티티에 핵심 비즈니스 로직이 존재하고 호출하는 것
    단순히 엔티티에 필요한 요청 위임하는 역할, 객체 지향 특성 적극 활용

  • 트랜잭션 스크립트 패턴
    엔티티에 비즈니스 로직 거의 없고 서비스 계층에서 대부분 비즈니스 로직을 처리하는 것

profile
Just Do IT ------- 🏃‍♀️

0개의 댓글