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()
)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로 한다.
🔗 코드 확인하기