[JPA 활용1] 2. 애플리케이션 구현 1

heyhey·2023년 2월 13일
0

Spring

목록 보기
19/23

회원 / 상품 / 주문 순으로 구현을 할 예정이다.

회원


회원 도메인

@Repository
public class MemberRepository {
    @PersistenceContext
    private EntityManager em;

    public void save(Member member) {
        em.persist(member);
    }
    public Member findOne(Long id) {
        return em.find(Member.class, id);
    }
    public List<Member> findAll() {
        return em.createQuery("select m from Member m", Member.class)
                .getResultList();
    }
    public List<Member> findByName(String name) {
        return em.createQuery("select m from Member m where m.name = :name", Member.class)
              .setParameter("name", name)
              .getResultList();
    } 
}
  • @Repository : 스프링 빈으로 등록
  • @PersistenceContext : 엔티티 매니저 (EntityManager) 주입
  • @PersistenceUnit : 엔티티 메니터 팩토리 주입

회원 서비스 개발

@Service
@Transactional(readOnly = true)
public class MemberService {
    @Autowired
    MemberRepository memberRepository;
/**
* 회원가입
*/
    @Transactional //변경
    public Long join(Member member) { 
      validateDuplicateMember(member); //중복 회원 검증
      memberRepository.save(member);
      return member.getId();
    }
    private void validateDuplicateMember(Member member) {
      List<Member> findMembers =
      memberRepository.findByName(member.getName());
      if (!findMembers.isEmpty()) {
          throw new IllegalStateException("이미 존재하는 회원입니다."); 
        }
    }
    /**
    *전체 회원 조회
    */
    public List<Member> findMembers() {
        return memberRepository.findAll();
    }
    public Member findOne(Long memberId) {
        return memberRepository.findOne(memberId);
    } 
}
  • @Transactional : 트랜잭션, 영속성 컨텍스트

  • readOnly = true : 데이터의 변경이 없는 읽기 전용 메서드에 사용한다. 성능향상이 된다 ( 그래서 읽기 전용에 다 적용한다. )

  • @Autowired: 생성자 injection에 많이 사용한다. 생성자가 하나면 생략해도 된다.
    => lombok을 이용하면 자동 주입 가능 (@RequiredArgsConstructor)

회원 기능 테스트

@RunWith(SpringRunner.class)
@SpringBootTest
@Transactional
public class MemberServiceTest {
    @Autowired MemberService memberService;
    @Autowired MemberRepository memberRepository;
    
    @Test
    public void 회원가입() throws Exception {
    //Given
        Member member = new Member();
        member.setName("kim");
    //When
        Long saveId = memberService.join(member);
    //Then
        assertEquals(member, memberRepository.findOne(saveId));
    }
    
    @Test(expected = IllegalStateException.class) 
    public void 중복_회원_예외() throws Exception {
        //Given
        Member member1 = new Member();
        member1.setName("kim");
        Member member2 = new Member();
        member2.setName("kim");

        //When
        memberService.join(member1); 
        memberService.join(member2); //예외가 발생해야 한다.

        //Then
        fail("예외가 발생해야 한다."); 
    }
}
  • @RunWith(SpringRunner.class) : 스프링과 테스트 통합
  • @SpringBootTest : 스프링부트 띄우고 테스트(@Autowired 용도)
  • @Transactional: 테스트가 끝나면 트랜잭션을 롤백

테스트 케이스는 격리된 환경에서 실행되고, 데이터를 초기화 해야 한다.
=> 테스트용 설정파일을 따로 추가하고, 테스트에서 스프링을 실행하면 이 위치의 설정 파일을 읽고, 없다면 java파일의 설정 파일을 읽는다.
test/resources/application.yml

loggin.level:
  org.hibernate.SQL : debug

복잡한 로직들이 많다 정신 차리자

주문 엔티티

@Entity
@Table(name = "orders")
@Getter @Setter
public class Order {
  @Id @GeneratedValue
  @Column(name = "order_id")
  private Long id;

  @ManyToOne(fetch = FetchType.LAZY) 
  @JoinColumn(name = "member_id") 
  private Member member; //주문 회원

  @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; //배송정보

  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 = 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;
    }
  }
profile
주경야독

0개의 댓글