일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 |
Tags
- BFS
- popleft
- 백준
- 인프런
- c#
- 소수판별
- spring
- unity
- 프로그래머스
- DP
- Python
- python3
- deque
- 1일1솔
- LCM
- 소수찾기
- 우선순위큐
- 합 구하기
- appendleft
- pypy3
- mvc
- 연관관계
- 누적합
- 완전탐색
- JPA
- C#강의
- 파이썬
- 그리디 알고리즘
- Java
- 브루투포스
Archives
- Today
- Total
jae_coding
[Spring Project] 주문 도메인 개발 본문
반응형
목차
- 주문 엔티티, 주문상품 엔티티
- 주문 Repository
- 주문 Service
- 주문 기능 Test
- 주문 검색 기능
1. 주문 엔티티, 주문상품 엔티티
Order Entity
package jpabook.jpashop.domain;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import javax.persistence.*;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
@Entity
@Table(name = "orders")
@Getter
@Setter
//생성메서드가 있을때 사용
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Order {
@Id @GeneratedValue
@Column(name = "order_id")
private Long id;
//다대일 관계
@ManyToOne(fetch = FetchType.LAZY)
//FK (연관관계의 주인)
@JoinColumn(name = "member_id")
private Member member;
//CascadeType.All 으로 인하여 각각 persist를해주는 것을 한줄로 간결하게 구현가능하다.
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
private List<OrderItem> orderItems = new ArrayList<>();
// order를 저장할 때, delivery도 persist를 같이해준다.
@OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
//FK (연관관계의 주인)
@JoinColumn(name = "delevery_id")
private Delivery delivery;
// hibernate 지원해줌 (주문시간)
private LocalDateTime orderDate;
@Enumerated(EnumType.STRING)
private OrderStatus status; // 주문상태 (ORDER, CANCEL)
//==연관관계 편의 메서드==//
public void setMember(Member member){
this.member = member;
member.getOrders().add(this);
}
public void addOrderItem(OrderItem orderItem){
orderItems.add(orderItem);
orderItem.setOrder(this);
}
public void setDelivery(Delivery delivery){
this.delivery = delivery;
delivery.setOrder(this);
}
//==생성 메서드==//
public static Order createOrder(Member member, Delivery delivery, OrderItem... orderItems){
//order생성
Order order = new Order();
//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 = orderItems.stream().mapToInt(OrderItem::getTotalPrice).sum();
// 같은 표현
int totalPrice = 0;
for (OrderItem orderItem : orderItems){
totalPrice += orderItem.getTotalPrice();
}
return totalPrice;
}
}
OrderItem Entity
package jpabook.jpashop.domain;
import jpabook.jpashop.domain.Item.Item;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import javax.persistence.*;
@Entity
@Getter
@Setter
//생성메서드가 있을 경우에 사용함
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class OrderItem {
@Id @GeneratedValue
@Column(name = "order_item_id")
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name="item_id")
private Item item;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "order_id")
private Order order;
private int orderPrice; //주문 가격
private int count; // 주문 수량
//==생성 메서드==//
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();
}
}
2. 주문 Repository
Order Repository
package jpabook.jpashop.repository;
import jpabook.jpashop.domain.Member;
import jpabook.jpashop.domain.Order;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;
import org.springframework.util.StringUtils;
import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.*;
import java.util.ArrayList;
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);
}
}
3. 주문 Service
Order Service
package jpabook.jpashop.service;
import jpabook.jpashop.domain.Delivery;
import jpabook.jpashop.domain.Item.Item;
import jpabook.jpashop.domain.Member;
import jpabook.jpashop.domain.Order;
import jpabook.jpashop.domain.OrderItem;
import jpabook.jpashop.repository.ItemRepository;
import jpabook.jpashop.repository.MemberRepository;
import jpabook.jpashop.repository.OrderRepository;
import jpabook.jpashop.repository.OrderSearch;
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;
//주문
@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();
}
// 주문 취소
@Transactional
public void cancelOrder(Long orderId){
//주문 엔티티 조회
Order order = orderRepository.findOne(orderId);
//주문 취소
order.cancel();
}
// 검색
public List<Order> findOrder(OrderSearch orderSearch){
return orderRepository.findAll(orderSearch);
}
}
4. 주문 Test
OrderTest
package jpabook.jpashop.service;
import jpabook.jpashop.domain.Address;
import jpabook.jpashop.domain.Item.Book;
import jpabook.jpashop.domain.Item.Item;
import jpabook.jpashop.domain.Member;
import jpabook.jpashop.domain.Order;
import jpabook.jpashop.domain.OrderStatus;
import jpabook.jpashop.exception.NotEnoughStockException;
import jpabook.jpashop.repository.OrderRepository;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.transaction.annotation.Transactional;
import javax.persistence.EntityManager;
import static org.junit.Assert.*;
@RunWith(SpringRunner.class)
@SpringBootTest
@Transactional
public class OrderServiceTest {
@Autowired
EntityManager em;
@Autowired
OrderService orderService;
@Autowired
OrderRepository orderRepository;
//상품주문
@Test
public void order() {
//given
Member member = createMember("회원1", new Address("서울", "강북구", "122-111"));
Book book = createBook("JPA", 10000, 10);
int orderCount = 2;
//when
Long orderId = orderService.order(member.getId(), book.getId(), orderCount);
//then
Order getOrder = orderRepository.findOne(orderId);
assertEquals("상품 주문시 상태는 ORDER", OrderStatus.ORDER, getOrder.getStatus());
assertEquals("주문한 상품 종류 수가 정확해야 한다.", 1, getOrder.getOrderItems().size());
assertEquals("주문 가격은 수량 * 수량이다", 10000 * 2, getOrder.getTotalPrice());
assertEquals("주문 수량만큼 재고가 줄어야한다.", 8, book.getStockQuantity());
}
//상품취소
@Test
public void cancelOrder() {
//given
Member member = createMember("회원1", new Address("서울", "강북구", "122-111"));
Book item = createBook("JPA", 10000, 10);
int orderCount = 2;
Long orderId = orderService.order(member.getId(), item.getId(), orderCount);
//when
orderService.cancelOrder(orderId);
//then
Order getOrder = orderRepository.findOne(orderId);
assertEquals("주문 취소시 상태는 CANCEL이다", OrderStatus.CANCEL, getOrder.getStatus());
assertEquals("주문이 취소된 후 재고가 원복되어야 한다.", 10, item.getStockQuantity());
}
//재고수량초과
@Test(expected = NotEnoughStockException.class)
public void exceedQuantity() {
//given
Member member = createMember("회원1", new Address("서울", "강북구", "122-111"));
Item item = createBook("JPA", 10000, 10);
int orderCount = 11;
//when
orderService.order(member.getId(), item.getId(), orderCount);
//then
fail("재고 수량 발생 예외가 발생해야 한다.");
}
private Book createBook(String name, int price, int stockQuantity) {
Book book = new Book();
book.setName(name);
book.setPrice(price);
book.setStockQuantity(stockQuantity);
em.persist(book);
return book;
}
private Member createMember(String name, Address address) {
Member member = new Member();
member.setName(name);
member.setAddress(address);
em.persist(member);
return member;
}
}
테스트 성공!
5. 주문 검색 기능
findAll(JPAQL)
/**
* JPAQL
*/
public List<Order> findAll(OrderSearch orderSearch) {
String jpql = "select o From Order o join o.member m";
boolean isFirstCondition = true;
//주문 상태 검색
if (orderSearch.getOrderStatus() != null) {
if (isFirstCondition) {
jpql += " where";
isFirstCondition = false;
} else {
jpql += " and";
}
jpql += " o.status = :status";
}
//회원 이름 검색
if (StringUtils.hasText(orderSearch.getMemberName())) {
if (isFirstCondition) {
jpql += " where";
isFirstCondition = false;
} else {
jpql += " and";
}
jpql += " m.name like :name";
}
TypedQuery<Order> query = em.createQuery(jpql, Order.class) .setMaxResults(1000); //최대 1000건
if (orderSearch.getOrderStatus() != null) {
query = query.setParameter("status", orderSearch.getOrderStatus());
}
if (StringUtils.hasText(orderSearch.getMemberName())) {
query = query.setParameter("name", orderSearch.getMemberName());
}
return query.getResultList();
}
findAll(JPA Criteria)
/**
* JPA Criteria
*/
public List<Order> findAllByCriteria(OrderSearch orderSearch) {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Order> cq = cb.createQuery(Order.class);
Root<Order> o = cq.from(Order.class);
Join<Order, Member> m = o.join("member", JoinType.INNER); //회원과 조인
List<Predicate> criteria = new ArrayList<>();
//주문 상태 검색
if (orderSearch.getOrderStatus() != null) {
Predicate status = cb.equal(o.get("status"),
orderSearch.getOrderStatus());
criteria.add(status);
}
//회원 이름 검색
if (StringUtils.hasText(orderSearch.getMemberName())) {
Predicate name =
cb.like(m.<String>get("name"), "%" +
orderSearch.getMemberName() + "%");
criteria.add(name);
}
cq.where(cb.and(criteria.toArray(new Predicate[criteria.size()])));
TypedQuery<Order> query = em.createQuery(cq).setMaxResults(1000); //최대 1000건
return query.getResultList();
}
//추후 쿼리 dsl로 변경할 것.
반응형
'Spring, java > Spring_Project' 카테고리의 다른 글
[Spring Project] 웹계층 개발 (0) | 2022.08.24 |
---|---|
[Spring Project] 상품 도메인 개발 (0) | 2022.08.22 |
[Spring Project] 회원 관리 및 테스트 (0) | 2022.08.22 |
[Spring Project] 애플리케이션 아키텍쳐 (0) | 2022.08.22 |
[Spring Project] 엔티티 설계 주의사항 (0) | 2022.08.22 |
Comments