회원 / 상품 / 주문 순으로 구현을 할 예정이다.
@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.ymlloggin.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;
}
}