인프런 - 스프링 부트와 JPA 활용1 by 김영한 을 기반으로 작성된 글입니다.
실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발
package jpabook.jpashop.repository;
import jpabook.jpashop.domain.Order;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;
import javax.persistence.EntityManager;
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);
}
}
1) 기술
@Repository
: 스프링 빈으로 등록 (Component 스캔 대상)@RequiredArgsConstructor
: final 필드만 생성자 생성1) 기능
이후 추가할 기능
package jpabook.jpashop.service;
import (생략)
@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class OrderService {
private final OrderRepository orderRepository;
private final MemberRepository memberRepository;
private final ItemRepository itemRepository;
/**
* 주문
*/
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);
OrderItem orderItem = OrderItem.createOrderItem(item, item.getPrice(), count);
위 코드를 아래 코드 처럼 개발이 가능하다.
OrderItem orderItem = new OrderItem();
orderItem.setItem(item);
orderItem.setOrderPrice(item.getPrice());
orderItem.setCount(count);
그러나 생성로직을 변경할 때 setter를 남발 하다보면
다양한 곳에서 값을 변경할 수 있어 객체의 일관성이 떨어진다.
엔티티에서 setter 사용 대신 생성자를 통해 파라미터를 넘기는게 좋다
💡 그래서 OrderItem.java 에 protected 생성자를 생성
OrderItem.java에 코드 추가
protected OrderItem() {}
protected 생성자를 일일히 생성하는 것 보다
파라미터가 없는 기본 생성자를 자동 생성 해주는
Lombok 어노테이션 @NoArgsConstructor
을 사용하는 것이 편리하다
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class OrderItem {
(생략)
}
@NoArgsConstructor(access = AccessLevel.PROTECTED)
를
엔티티 클래스 위에 선언하여 간략하게 protected 생성자를 생성할 수 있다
//주문 생성
Order order = Order.createOrder(member, delivery, orderItem);
//주문 저장
orderRepository.save(order);
return order.getId();
}
delivery와 orderItem 을 따로 생성 / persist 하지 않고
orderRepository.save(order); 의 코드로 바로 save() 해준 이유는 무엇일까 ❓
바로 Order.java의 orderItems 의 cascade 때문이다
order.java
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
private List<OrderItem> orderItems = new ArrayList<>();
@OneToOne(fetch = LAZY, cascade = CascadeType.ALL )
@JoinColumn(name="delivery_id")
private Delivery delivery;
cascade = CascadeType.ALL
을 설정해주었기 때문에
orderItem, delivery 둘 다 컬렉션에 있는 모든 엔티티를 자동으로 persist 해준다
부모 엔티티가 영속화될때, 자식 엔티티도 같이 영속화되고
부모 엔티티가 삭제 될때, 자식 엔티티도 삭제되는 등
부모의 영속성 상태가 전이되는 것을 이야기한다
출처: https://coding-start.tistory.com/159 [코딩스타트]
/**
* 주문 취소
*/
@Transactional
public void cancelOrder(Long orderId) {
//주문 엔티티 조회
Order order = orderRepository.findOne(orderId);
//주문 취소
order.cancel();
}
Transaction 안에 Entity 변경 발생시
변경 내용을 자동으로 DB에 반영하고 Transaction을 commit하는 JPA 특징
도메인 모델 패턴
엔티티에 핵심 비즈니스 로직이 존재하고 호출하는 것
단순히 엔티티에 필요한 요청 위임하는 역할, 객체 지향 특성 적극 활용
트랜잭션 스크립트 패턴
엔티티에 비즈니스 로직 거의 없고 서비스 계층에서 대부분 비즈니스 로직을 처리하는 것