[ 구현 기능 ]
- 상품 주문
- 주문 내역 조회
- 주문 취소
[ 구현 순서 ]
- 주문 엔티티, 주문상품 엔티티 개발 > 핵심 비즈니스 로직 추가
- 주문 리포지토리 개발
- 주문 서비스 개발
- 주문 검색 기능 개발
- 주문 기능 테스트
...
@Entity
@Table(name = "orders")
@Getter @Setter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
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(DeliveryStatus.COMP.equals(this.getDelivery().getStatus())){
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;
}
}
...
@Entity
@Table(name = "order_item")
@Getter @Setter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
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();
}
}
OrderEntity
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;
}
OrderItem Entity
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;
}
removeStock()
을 호출해서 주문한 수량만큼 상품의 재고를 줄임new OrderItem()
을 사용해서 새로운 주문 상품을 생성하지 못하도록 해야함 protected OrderItem(){
}
protected Order(){
}
entity
에 코드를 직접 추가하는 방법도 있지만, Lombok
어노테이션을 활용하면 더 간단하게 표현 가능@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class OrderItem {
@Getter @Setter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Order {
AccessLevel
을 Protected
로 설정하면 서비스 단에서 new
로 주문 상품 객체를 생성할 수 없도록 막을 수 있음order Entity
public void cancel(){
if(this.getDelivery().getStatus().equals(DeliveryStatus.COMP)){
throw new IllegalStateException("이미 배송완료된 상품은 취소가 불가능합니다.");
}
this.setStatus(OrderStatus.CANCEL);
for(OrderItem orderItem : orderItems){
orderItem.cancel();
}
}
OrderItem Entity
public void cancel() {
getItem().addStock(count);
}
addStock()
을 호출해서 취소한 주문 수량만큼 상품의 재고를 증가시킴Order Entity
public int getTotalPrice(){
int totalPrice = 0;
for (OrderItem orderItem : orderItems){
totalPrice += orderItem.getTotalPrice();
}
return totalPrice;
}
OrderItem Entity
public int getTotalPrice(){
return getOrderPrice() * getCount();
}
package jpabook.jpashop.repository;
import jakarta.persistence.EntityManager;
import jpabook.jpashop.domain.Order;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;
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);
}
// public List<Order> findAll(OrderSearch orderSearch){}
}
package jpabook.jpashop.service;
import jpabook.jpashop.domain.*;
import jpabook.jpashop.repository.ItemRepository;
import jpabook.jpashop.repository.MemberRepository;
import jpabook.jpashop.repository.OrderRepository;
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 OrderService {
private final OrderRepository orderRepository;
private final MemberRepository memberRepository;
private final ItemRepository itemRepository;
/**
* 주문 저장
* @param memberId, itemId, count
* @return Long
*/
@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());
//주문상품 생성
OrderItem orderItem = OrderItem.createOrderItem(item, item.getPrice(), count);
//주문 생성
Order order = Order.createOrder(member, delivery, orderItem);
//주문 저장
orderRepository.save(order);
return order.getId();
}
/**
* 주문 취소
* @param orderId
*/
@Transactional
public void cancelOrder(Long orderId){
//주문 엔티티 조회
Order order = orderRepository.findOne(orderId);
//주문 취소
order.cancel();
}
// public List<Order> findOrders(OrderSearch orderSearch){
// return orderRepository.findAll(orderSearch);
// }
}
@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());
//주문상품 생성
OrderItem orderItem = OrderItem.createOrderItem(item, item.getPrice(), count);
//주문 생성
Order order = Order.createOrder(member, delivery, orderItem);
//주문 저장
orderRepository.save(order);
return order.getId();
}
Order Entity
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
private List<OrderItem> orderItems = new ArrayList<>();
@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@JoinColumn(name = "delivery_id")
private Delivery delivery;
Cascade
를 설정해놓았기 때문에, order가 persist될 때 orderItem과 delivery도 강제로 persist를 해줌Order Service
@Transactional
public Long order(Long memberId, Long itemId, int count){
...
//주문 저장
orderRepository.save(order);
...
}
Persist
해야 하는 Life Cycle
도 완전히 동일하기 때문에 위 방식으로 구현이 가능함cascade
를 사용하면 안되고, 별도의 리포지토리를 생성해서 따로 관리를 해줘야 함 @Transactional
public void cancelOrder(Long orderId){
//주문 엔티티 조회
Order order = orderRepository.findOne(orderId);
//주문 취소
order.cancel();
}
OrderSearch
라는 검색 조건을 가진 객체로 주문 엔티티를 검색