Skip to content

[JPA study 5주차 이지은]

ljinny edited this page May 13, 2024 · 1 revision

5주차 정리

수강 강의

  • 섹션 6. 주문 도메인 개발
  • 주문, 주문상품 엔티티 개발
  • 주문 리포지토리 개발
  • 주문 서비스 개발
  • 주문 기능 테스트
  • 주문 검색 기능 개발

주문 도메인 개발

  • 구현 기능
    • 상품 주문 (회원, 상품, 수량 선택해서 주문)

      Untitled

    • 주문 내역 조회

      Untitled

      Untitled

    • 주문 취소

  • 순서
    • 주문 엔티티, 주문 상품 엔티티 개발 (핵심 비즈니스 로직 추가)
    • 주문 리포지토리 개발
    • 주문 서비스 개발
    • 주문 검색 기능 개발
    • 주문 기능 테스트

주문, 주문 상품 엔티티 개발

생성 메서드

  • domain/Order.java

    Untitled

    • OrderItem… : 여러개를 매개변수로 받아줌
    • order를 생성해서 Member, Delivery를 받아온 매개변수로 set해주고 foreach문을 사용해서 OrderItem set
    • status와 time도 설정
    • order를 리턴해줌

    생성 메서드 사용 ⇒ 변경 사항 생길 시 이 부분만 변경해주면 됨

  • OrderItem.java

    생성 메서드 필요

    Untitled

    • 주문 상품, 가격, 수량 정보를 사용해서 주문상품 엔티티를 생성한다. 그리고 item.removeStock(count)를 호출해서 주문한 수량만큼 상품의 재고를 줄인다

비즈니스 로직

  • domain/Order.java

    Untitled

    • 배송 완료된 상품은 취소 불가능 → if 문으로 exception throw

    • if문 통관 → 취소 가능 ⇒ setStatus로 OrderStatus를 cancle로 바꿔줘도 됨

    • 1개의 Order에 여러개의 orderitem을 주문한 경우

      → 각각의orderitem도 cancle을 해야 함 ⇒ OrderItem.java에 비즈니스 로직 추가

  • domain/OrderItem.java

    Untitled

    • 목표 : 재고를 올리는 것(취소한 수량 == 주문 수량만큼)
    • 주문 수량 변수인 count와 addStock 이용해서 주문수량만큼 되돌려줌

조회 로직

  • Order.java

    Untitled

    Untitled

    똑같은 로직인데 sum을 이용해서 이렇게 수정 가능

  • OrderItem.java

    Untitled

    • 주문 가격에 수량을 곱한 값을 반환

주문 리포지토리 개발

Untitled

  • 다른 리포지토리들과 같게 save, findOne 구현

주문 서비스 개발

  • OrderService.java

    Untitled

    OrderRepository, MemberRepositry, ItemRepository 모두 의존

    • 주문 : 주문하는 회원 식별자, 상품 식별자, 주문 수량 정보를 받아서 실제 주문 엔티티를 생성한 후 저장

      • 예제 단순화를 위해 주문시 OrderItem은 하나만 넘기도록 구성

      Untitled

      • 배송정보 → member의 주소 정보를 이용해서 생성
      • 주문 상품 → OrderItem의 생성 메서드 사용
      • 주문 생성 → Order의 생성 메서드 사용
      • 주문 저장 → Order에서 cascade를 사용했기 때문에 order만 save해도 전부 다 저장됨

      Untitled

      이런 식으로 생성하는 거 막는 방법(Order도 마찬가지)

      → OrderItem.java에 생성자를 protected로 설정(@NoArgsConstructor의 AccessLevel을 PROTECTED롤 설정해서 똑같은 효과를 낼 수 있음)

      Untitled

      Untitled

      ⇒ 생성 불가능

    • 취소 : 주문 식별자를 받아서 주문 엔티티를 조회한 후 주문 엔티티에 주문 취소를 요청

      Untitled

      • 주문 엔티티를 조회해서 Order의 cancle을 호출해서 주문 취소
    • 검색

      Untitled

    • 비즈니스 로직의 대부분 → 엔티티

      • 서비스 계층은 단순히 엔티티에 필요한 요청을 위임하는 역할

      ⇒ 도메인 모델 패턴(엔티티가 비즈니스 로직을 가지고 객체 지향의 특성을 적극 활용하는 것)

      • 반대로 엔티티에는 비즈니스 로직이 거의 없고 서비스 계층에서 대부분의 로직을 처리하는 것 : 트랜잭션 스크립트 패턴

상품 주문 테스트

  • 상품주문, 주문취소, 재고수량초과 테스트

  • 상품 주문

    Untitled

    Untitled

    • 주문 상태 변경
    • 주문 상품 종류(OrderItem의 사이즈)
    • total price
    • 재고 수량 감소

    → assertEquals로 확인

    Untitled

    • 비교할 재고 수량을 10개로 하면 같지 않기 때문에 X가 뜸

Untitled

원활한 테스트를 위해 해당 부분을 세트로 묶음(ctrl + alt + M)

⇒ createMember

Untitled

  • 재고 수량 초과

    재고 수량을 초과해서 주문 → NotEnoughStockException 예외가 발생해야 함

    Untitled

    • createBook에서 stockQuantity = 10으로 설정
    • int orderCount = 11으로 재고 수량보다 많은 양이 주문됨
      • when절의 orderService.order에서 주문

        → Item.java에서 removeStock을 할 때 예외가 발생

        Untitled

  • 주문 취소

    Untitled

    • 주문을 취소 하는 경우

      1. 주문의 status가 CANCLE로 변경되어야 함

      2. 주문을 취소한 만큼 재고 수량을 증가시켜 주어야 함

    ⇒ assertEquals를 이용하여 getOrder의 status가 CANCLE인지 확인, item의 stockQuantity가 10인지 확인(원래 10, 주문시 -2하여 8, 취소시 다시 10)

주문 검색 기능 개발

핵심 : JPA에서 동적 쿼리를 어떻게 해결해야 하는가?

  • 주문 상태에서 회원명과 주문 상태로 검색 가능

    → 필터링 조건 ⇒ 동적 쿼리 사용해야 함

    주문 상태 → ORDER, CANCLE

    • 주문 상태로 검색

    Untitled

    Untitled

    • 회원 명으로 검색

    Untitled

  • repository/OderSearch.java (검색 조건 파라미터)

    Untitled

    → OrderRepository에서 주석처리 해뒀던 부분 구현

    : findAll(OderSearch orderSearch) 메서드 - 검색 조건에 동적으로 쿼리를 생성해서 주문 엔티티를 조회

    Untitled

    • 회원 이름과 주문 상태 값이 다 있다는 가정하에 작성된 코드
    • 이름이 없거나 주문상태가 없으며 필터링 없이 모두 가져와야 함

    JPQL로 처리

    public List<Order> findAllByString(OrderSearch orderSearch) {
     //language=JPAQL
     String jpql = "select o From Order o join o.member m";
     boolean isFirstCondition = true;
     
     //주문 상태 검색
     if (orderSearch.getOrderStatus() != null) { // 주문 상태 값이 있으면
    	 if (isFirstCondition) {
    		 jpql += " where"; // 처음 조건이면 where를 붙여아함
    		 isFirstCondition = false;
    	 } else {
    		 jpql += " and"; // 중간 조건이면 and를 붙여야함
    	 }
    	 jpql += " o.status = :status"; // jql을 동적으로 빌트
     }
     //회원 이름 검색
     if (StringUtils.hasText(orderSearch.getMemberName())) { // hasText
    	 if (isFirstCondition) {
    		 jpql += " where";
    		 isFirstCondition = false;
    	 } else {
    		 jpql += " and";
    	 }
    	 jpql += " m.name like :name"; // name like를 동적으로 넣어줌
     }
     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();
    }

    문자를 더해서 계산하는건 번거롭고 실수로 인한 버그가 발생하기 쉬움

    JPA Criteria로 처리

    jpql을 자바코드로 작성할 수 있게 jpa에서 표준으로 제공하는 것

    실무에서 사용 X (실무에서 사용하기 너무 복잡함)

    Untitled

    • 유지 보수성이 낮음

      → 다음 장에서 Querydsl로 해결

Clone this wiki locally