본문 바로가기

Advance I/Springboot & JPA

22.05.14

주문 리포지토리 개발

public List findAll(OrderSearch orderSearch) { ... }

주문 엔티티를 검색하는 기능. 검색 조건으로 OrderSearch 을 사용한다.

주문 서비스 개발

MemberRepository, ItemRepository, OrderRepository 필요

  • order(): 클라이언트가 주문 버튼을 눌렀을 때 세팅된 데이터를 반영하는 Transaction. 실제 주문 엔티티를 생성하고 저장한다.
  • cancelOrder() : 주문 식별자를 받아서 주문 엔티티를 조회한 후 주문 엔티티에 주문 취소를 요청한다.

*Transaction: 데이터베이스의 상태를 변화시키기 해서 수행하는 작업의 단위

 

@Entity
@Table(name="orders")
public class Order{
    ...
    @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;
    ...
}

 

원래는 배송정보(Delivery), 주문상품(OrderItem) 등 새로 생성된 모든 엔티티는 persist 후 다른 엔티티의 필드로 세팅되어야 한다.

그러나 주문 엔티티의 cascade 옵션에 의해 주문을 persist하면 연관된 엔티티를 자동으로 persist 해준다.

 

cascade의 범위

명확하진 않으나 '관리'하는 객체, 다른 데서 참조하지 않는 객체를 참조하는 주인에서만 사용하는 것이 좋다.

persist 해야할 라이프사이클이 같을 때 ex) 배송정보, 주문상품은 주문 엔티티에서만 사용한다.

이런 개념에 대해 감이 오지 않으면 아예 쓰지 않고 나중에 리팩토링을 통해 사용하는 것이 좋은 방법일 수 있다.

 

protected 생성자 정의 : 생성 메서드가 아닌 생성자를 통한 엔티티 객체 생성 막기

@NoArgsConstructor(access=AccessLevel.PROTECTED) = protected ClassName(){}

  • JPA에서 엔티티는 반드시 파라미터가 없는 생성자가 있어야 하고, 이는 public 또는 protected 여야 한다.
  • JPA에서 protected의 경우 생성자를 통해 생성하면 안된다는 의미로 통한다.

엔티티의 필드가 변경되면 JPA가 알아서 update 쿼리를 날려서 변경사항을 데이터베이스에 반영시킨다.

 

참고: 주문 서비스의 주문과 주문 취소 메서드를 보면 비즈니스 로직 대부분이 엔티티에 있다. 서비스 계층은 단순히 엔티티에 필요한 요청을 위임하는 역할을 한다. 이처럼 엔티티가 비즈니스 로직을 가지고 객체 지향의 특성을 적극 활용하는 것을 도메인 모델 패턴이라 한다. 반대로 엔티티에는 비즈니스 로직이 거의 없고 서비스 계층에서 대부분 의 비즈니스 로직을 처리하는 것을 트랜잭션 스크립트 패턴이라 한다.

무엇이 더 좋다기보다 애플리케이션에 따라 어떤 패턴이 유지보수에 좋은지 고민하며 사용하자. 물론 한 애플리케이션 내에서 양립 가능하다. 문맥에 따라 판단할 것.

주문 기능 테스트

테스트 요구사항

  • 상품 주문이 성공해야 한다.
  • 상품을 주문할 때 재고 수량을 초과하면 안 된다.
  • 주문 취소가 성공해야 한다.
@Test(expected = NotEnoughStockException.class)
public void 상품주문_재고수량초과() throws Exception {

    //Given
    Member member = createMember();
    Item item = createBook("시골 JPA", 10000, 10); //이름, 가격, 재고
    int orderCount = 11; //재고보다 많은 수량
   
    //When
    orderService.order(member.getId(), item.getId(), orderCount);
 
    //Then
    fail("재고 수량 부족 예외가 발생해야 한다.");
}

주문 검색 기능 개발

동적 쿼리 구현 필요

 

JPA에서 동적 쿼리 구현하기

1. JPQL(컨디션에 따른 조건문으로 쿼리문 조립): 번거롭고, 실수로 인한 버그가 충분히 발생할 수 있다.

2. JPA Criteria: JPA 표준 스펙이지만 실무에서 사용하기에 너무 복잡하다.

3. Querydsl

 

검색 조건 파라미터: OrderSearch { String memberName, OrderStatus orderStatus }

  회원 이름과 주문 상태[ORDER, CANCEL] 로 필터링

 

*메서드 내부의 변수->파라미터화: ctrl+alt+P

*본 메서드-테스트 메서드 간 이동: ctrl+shift+T

'Advance I > Springboot & JPA' 카테고리의 다른 글

22.05.18  (0) 2022.05.19
22.05.17  (0) 2022.05.17
22.05.13  (0) 2022.05.13
22.05.11  (0) 2022.05.11
22.05.10  (0) 2022.05.10