[JPA 활용1] 주문 도메인 개발
                                            
                                                
                                                
                                                
                                                
                                                
                                                 23272 단어  TILSpringbootJPAJPA
                    
[ 주문, 주문상품 엔티티 개발 ]
1. Order.java
public class Order {
    ...
    
    //==생성 메서드==//
    public static Order createOrder(Member member, Delivery delivery, OrderItem... orderItems) {
        Order order = new Order();
        order.setMember(member);
        order.setDelivery(delivery);
        
        for (OrderItem orderItem : orderItems) {
            order.addOrderItem(orderItem);
        }
        
        order.setStatus(OrderStatus.ORDER);
        order.setOrderDate(LocalDateTime.now());
        return order;
    }
    //==비즈니스 로직==//
    /**
     * 주문 취소
     */
    public void cancel() {
        if (delivery.getStatus() == DeliveryStatus.COMP) {
            throw new IllegalStateException("이미 배송완료된 상품은 취소가 불가능합니다.");
        }
        this.setStatus(OrderStatus.CANCEL);
        for (OrderItem orderItem : orderItems) {
            orderItem.cancel();
        }
    }
    //==조회 로직==//
    /**
     * 전체 주문 가격 조회
     */
    public int getTotalPrice() {
        int totalPrice = 0;
        for (OrderItem orderItem : orderItems) {
            totalPrice += orderItem.getTotalPrice();
        }
        return totalPrice;
    }
}생성 메서드 ( createOrder() )
- 주문 엔티티를 생성할 때 사용한다.
- 주문 회원, 배송 정보, 주문 상품의 정보를 받아서 실제 주문 엔티티를 생성한다.
주문 취소 ( cancel() )
- 주문 취소시 사용한다.
- 주문 상태를 취소로 변경하고 주문 상품에 주문 취소를 알린다.
- 만약 이미 배송을 완료한 상품이면 주문을 취소하지 못하도록 예외를 발생시킨다.
전체 주문 가격 조회
- 주문 시 사용한 전체 주문 가격을 조회한다. 전체 주문 가격을 알려면 각각의 주문 상품 가격을 알아야 한다.
- 로직을 보면 연관된 주문상품들의 가격을 조회해서 더한 값을 반환한다. (실무에서는 주로 주문에 전체 주문 가격 필드를 두고 역정규화 한다.)
2. OrderItem.java
public class OrderItem {
    ...
    
    //==생성 메서드==//
    public static OrderItem createOrderItem(Item item, int orderPrice, int count) {
        OrderItem orderItem = new OrderItem();
        orderItem.setItem(item);
        orderItem.setOrderPrice(orderPrice);
        orderItem.setCount(count);
        item.removeStock(count);
        return orderItem;
    }
    //==비즈니스 로직==//
    /**
     * 주문 취소
     */
    public void cancel() {
        getItem().addStock(count);
    }
    //==조회 로직==//
    /**
     * 주문상품 전체 가격 조회
     */
    public int getTotalPrice() {
        return getOrderPrice() * getCount();
    }
}생성 메서드 ( createOrderItem() )
- 주문 상품, 가격, 수량 정보를 사용해서 주문 상품 엔티티를 생성한다.
- item.removeStock(count)를 호출해서 주문한 수량만큼 상품의 재고를 줄인다.
주문 취소 ( cancel() )
- getItem().addStock(count)를 호출해서 취소한 주문 수량만큼 상품의 재고를 증가시킨다.
주문 가격 조회 ( getTotalPrice() )
- 주문 가격 * 수량을 반환한다.
[ 주문 리포지토리 개발 ]
OrderRepository.java
@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);
    }
    
    // public List<Order> findAll(OrderSearch orderSearch) { ... }
}- 주문 리포지토리는 주문 엔티티를 저장하고 (save()) 검색하는 (findOne(),findAll()) 기능이 있다.
- findAll()은 뒤에서 자세히 알아볼 예정!
[ 주문 서비스 개발 ]
OrderService.java
@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class OrderService {
    private final OrderRepository orderRepository;
    private final MemberRepository memberRepository;
    private final ItemRepository itemRepository;
    /**
     * 주문
     */
    @Transactional
    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());
        delivery.setStatus(DeliveryStatus.READY);
        //주문상품 생성
        OrderItem orderItem = OrderItem.createOrderItem(item, item.getPrice(), count);
        //주문 생성
        Order order = Order.createOrder(member, delivery, orderItem);
        //주문 저장
        orderRepository.save(order);
        return order.getId();
    }
    /**
     * 주문 취소
     */
    @Transactional
    public void cancelOrder(Long orderId) {
        //주문 엔티티 조회
        Order order = orderRepository.findOne(orderId);
        //주문 취소
        order.cancel();
    }
    /**
     * 주문 검색
     */
//    public List<Order> findOrders(OrderSearch orderSearch) {
//        return orderRepository.findAll(orderSearch);
//    }
}- 주문 서비스는 주문 엔티티와 주문 상품 엔티티의 비즈니스 로직을 활용해서 주문, 주문 취소, 주문 내역 검색 기능을 제공한다.
- 예제를 단순화하기 위해서 한 번에 하나의 상품만 주문할 수 있도록 하였다.
주문 ( order() )
- 주문하는 회원 식별자, 상품 식별자, 주문 수량 정보를 받아서 실제 주문 엔티티를 생성한다.
주문 취소 ( cancelOrder() )
- 주문 식별자를 받아서 주문 엔티티를 조회한 후 주문 엔티티에 주문 취소를 요청한다.
주문 검색 ( findOrders() )
- OrderSearch라는 검색 조건을 가진 객체로 주문 엔티티를 검색한다.
- 뒤에서 더 자세히 알아볼 예정!
📌 참고
- 도메인 모델 패턴
: 엔티티가 비즈니스 로직을 가지고 객체 지향의 특성을 적극 활용하는 것을 뜻한다.
- 주문 서비스의 주문(
order())과 주문 취소(cancelOrder()) 메서드를 보면 비즈니스 로직 대부분이 엔티티에 있다.- 서비스 계층은 단순히 엔티티에 필요한 요청을 위임하는 역할을 한다.
- 트랜잭션 스크립트 패턴
: 엔티티에는 비즈니스 로직이 거의 없고 서비스 계층에서 대부분의 비즈니스 로직을 처리하는 것
[ 주문 기능 테스트 ]
테스트 요구사항
- 상품 주문이 성공해야 한다.
- 상품을 주문할 때 재고 수량을 초과하면 안 된다.
- 주문 취소가 성공해야 한다.
실행 결과

[ 주문 검색 기능 개발 ]
💡 JPA로 동적쿼리를 해결하는 방법
1. 문자열 조합: 조건에 따라 문자열을 결합하면서 query문을 만들고 parameter를 세팅해주는 방법
2. JPA Criteria: JPA 표준 스펙에서 제공하는 기능
3. queryDSL: 오픈소스를 통해 제공되는 기능으로 쿼리 구현을 method로 한다.
🔗 코드 확인하기
Author And Source
이 문제에 관하여([JPA 활용1] 주문 도메인 개발), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@mmy789/JPA-활용1-주문-도메인-개발저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
                                
                                
                                
                                
                                
                                우수한 개발자 콘텐츠 발견에 전념
                                (Collection and Share based on the CC Protocol.)