제육's 휘발성 코딩
반응형

상품 도메인 개발

구현 기능

  • 상품 등록
  • 상품 목록 조회
  • 상품 수정

순서

  • 상품 엔티티 개발 (비즈니스 로직 추가)
  • 상품 리포지토리, 서비스 개발
  • 상품 기능 테스트

비즈니스 로직 추가

package jpabook.jpashop.domain;

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<>();

    // 비즈니스 로직 추가
    public void addStock(int quantity) {
        this.stockQuantity += quantity;
    }

    public void removeStock(int quantity) {
        int restStock = this.stockQuantity - quantity;
        if (restStock < 0) {
            throw new NotEnoughStockException("need more stock");
        }
        this.stockQuantity = restStock;
    }
}
  • Item 엔티티에서 관리하는 stockQuantity를 변경하는 비즈니스로직을 엔티티에서 처리하자. 서비스 단에서 처리할 수 있지만, 엔티티에서 처리하는 것이 응집도가 높은 설계로 볼 수 있다.
  • 현재는 setter를 통해 외부에서 수정할 수 있게 되어있지만, setter 없이 지금처럼 추가적인 비즈니스로직을 만드는 것이 바람직하다.
package jpabook.jpashop.exception;

public class NotEnoughStockException extends RuntimeException{

    public NotEnoughStockException() {
    }

    public NotEnoughStockException(String message) {
        super(message);
    }

    public NotEnoughStockException(String message, Throwable cause) {
        super(message, cause);
    }

    public NotEnoughStockException(Throwable cause) {
        super(cause);
    }

    public NotEnoughStockException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }
}
  • 커스텀 예외를 처리하기 위해 별도의 exception 디렉토리를 만들어서 RuntimeException 을 상속받아 구현체들을 만들자.

상품 리포지토리 개발

package jpabook.jpashop.repository;

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

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

@Repository
@RequiredArgsConstructor
public class ItemRepository {
    // @PersistContext 대신에 @Autowired로 처리
    private final EntityManager em;

    public void save(Item item) {
        if (item.getId() == null) {
            em.persist(item);
        } else {
            em.merge(item); //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).getResultList();
    }
}
  • @PersistenceContext 대신에 @Autowired로 처리가 가능하다.
  • em.merge는 저장된 엔티티를 수정한다고 보고한다고 생각하면 된다.

상품 서비스 개발

package jpabook.jpashop.service;

import jpabook.jpashop.domain.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
    public void saveItem(Item item) {
        itemRepository.save(item);
    }

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

    public Item findOne(Long itemId) {
        return itemRepository.findOne(itemId);
    }
}
  • 상품 서비스는 상품 리포지토리에 단순히 위임만 하는 클래스

본 포스팅은 '인프런 - 김영한님 강의(실전! 스프링 부트와 JPA 활용1)'를 토대로 정리한 내용입니다.

반응형
profile

제육's 휘발성 코딩

@sasca37

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요! 맞구독은 언제나 환영입니다^^