프로그래밍/spring boot

[스프링부트] 실전! 스프링 부트와 JPA 활용1 #5 상품 도메인 개발

aSpring 2023. 11. 3. 09:37
728x90
728x90
※ 본 포스팅은 김영한 강사님의 인프런 '실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발' 강의를 들으며 작성한 수강일지 입니다.

 

| 상품 도메인 개발

1. 상품 엔티티 개발(비즈니스 로직 추가)
2. 상품 리포지토리 개발
3. 상품 서비스 개발
4. 상품 기능 테스트(생략)

 

구현 기능
  • 상품 등록
  • 상품 목록 조회
  • 상품 수정

 

1. 상품 엔티티 개발(비즈니스 로직 추가)

비즈니스 로직 추가

- 도메인 자체에 비즈니스 로직 추가

도메인 주도 설계 : 이 엔티티 자체가 해결할 수 있는 것들은 주로 엔티티 안에 비즈니스 로직을 넣는 게 좋다 -> 객체지향적(그래야 응집력이 좋음)

package jpabook.jpashop.domain.item;

import jpabook.jpashop.domain.Category;
import jpabook.jpashop.exception.NotEnoughStockException;
import lombok.Getter;
import lombok.Setter;

import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "dtype")
@Getter @Setter
public abstract class Item {

    @Id @GeneratedValue
    @Column(name = "item_id")
    private Long id;

    private String name;
    private int price;
    private int stockQuantity;

    @ManyToMany(mappedBy = "items")
    private List<Category> categories = new ArrayList<Category>();

    //==비즈니스 로직==//
    /**
    * stock 증가 - 객체 지향적으로 생각해 봤을 때, 해당 데이터(stockQuantity)를 가지고있는 쪽에 비즈니스 메서드가 있는게 가장 좋음(그래야 응집력이 있음)
    */
    public void addStock(int quantity) {
        this.stockQuantity += quantity;
    }

    /**
     * stock 감소
     */
    public void removeStock(int quantity) {
        int restStock = this.stockQuantity - quantity;
        if (restStock < 0) {
            throw new NotEnoughStockException("need more stock");
        }
        this.stockQuantity = restStock;
    }
}

 

예외 추가

src/main/java/jpabook.jpashop/exception/NotEnoughStockException

- Command + N -> Override Methods -> 오버라이드 할 메서드들 선택

 

 

비즈니스 로직 분석

  • addStock() 메서드 :  파라미터로 넘어온 수만큼 재고를 늘림. 이 메서드는 재고가 증가하거나 상품 주문을 취소해서 재고를 다시 늘려야 할 때 사용
  • removeStock() 메서드 : 파라미터로 넘어온 수만큼 재고를 줄임. 만약 재고가 부족하면 예외가 발생함. 주로 상품을 주문할 때 사용.

 

2. 상품 리포지토리 개발

package jpabook.jpashop.repository;

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

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

@Repository
@RequiredArgsConstructor
public class ItemRepository {

    private final EntityManager em;

    //상품 저장
    public void save(Item item) {
        // item은 jpa가 저장하기 전까지 id 값이 없기 때문(완전히 새로 생성하는 객체)
        if (item.getId() == null) {
            em.persist(item); // JPA가 제공하는 persist로 아이템을 신규로 등록
        } else { // 이미 존재한다면
            em.merge(item); // 진짜 update는 아니지만 update라고 이해하면 됨
        }
    }

    //상품 단건 조회
    public Item findOne(Long id) {
        return em.find(Item.class, id);
    }

    //여러개 조회
    public List<Item> findAll() {
        return em.createQuery("select i from Item i", Item.class) // JPQL
                .getResultList();
    }
}

기능 설명

  • save()
    • id가 없으면 신규로 보고 persist() 실행
    • id가 있으면 이미 데이터베이스에 저장된 엔티티를 수정한다고 보고, merge()를 실행(update 개념은 아님)

 

3. 상품 서비스 개발

- 상품 서비스는 상품 리포지토리에 단순히 위임만 하는 클래스

package jpabook.jpashop.service;

import jpabook.jpashop.domain.item.Item;
import jpabook.jpashop.repository.ItemRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

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

    private final ItemRepository itemRepository;

    @Transactional // readOnly = true가 되어있어서 저장이 되지 않으므로, 얘는 저장해야해서 추가(우선순위가 높음)
    public void saveItem(Item item) {
        itemRepository.save(item);
    }

    public List<Item> findItems() {
        return itemRepository.findAll();
    }

    public Item findOne(Long itemId) {
        return itemRepository.findOne(itemId);
    }

}

 

4. 상품 기능 테스트

회원 테스트와 비슷하므로 생략

728x90
728x90