[SPRING] 스프링 JPA 도메인 설계 #1(도메인 설계 및 테스트 코드)
스프링 JPA SHOP 강의
JPA 사용 기초와 도메인 간의 관계에 대해서 학습
1장. 도메인 설계
1-1장. JPA SHOP 도메인 설계
1-2장. DOMAIN 코드
Member
package jpabook.jpashop.domain;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
@Entity
@Getter @Setter
public class Member {
@Id @GeneratedValue
@Column(name = "member_id")
private Long id;
private String name;
@Embedded
private Address address;
@OneToMany(mappedBy = "member")
private List<Order> orders = new ArrayList<>();
}
Address
package jpabook.jpashop.domain;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import javax.persistence.*;
@Embeddable
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class Address {
private String city;
private String street;
private String zipcode;
}
Order
package jpabook.jpashop.domain;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import javax.persistence.*;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
@Entity
@Table(name = "orders")
@Getter @Setter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
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(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
//cascade를 걸어주면 order가 persist할 때 해당되는 곳들도 같이 동작시켜준다
@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;
*/
return orderItems.stream().mapToInt(OrderItem::getTotalPrice).sum();
}
}
Delivery
package jpabook.jpashop.domain;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;
@Entity
@Getter @Setter
public class Delivery {
@Id @GeneratedValue
@Column(name = "delivery_id")
private Long id;
@OneToOne(mappedBy = "delivery", fetch = FetchType.LAZY)
private Order order;
@Embedded
private Address address;
@Enumerated(EnumType.STRING)
private DeliveryStatus status; //READY, COMP
}
OrderItem
package jpabook.jpashop.domain;
import jpabook.jpashop.domain.item.Item;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import javax.persistence.*;
@Entity
@Getter
@Setter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class OrderItem {
@Id
@GeneratedValue
@Column(name = "order_item_id")
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "item_id")
private Item item;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "order_id")
private Order order;
private int orderPrice;//주문가격
private int count;//주문 수량
/*
//대체로 @NoArgsConstructor(access = AccessLevel.PROTECTED) 이렇게도 많이 사용한다
protected OrderItem(){
1. 이렇게 protected 생성자를 만들어주면 다른 곳에서 new OrderItem()으로 못 만든다
2. 그렇다면 다른곳에서 OrderItem을 만들기 위해서는 createOrderItem()을 사용할 수 밖에 없다.
3. 이렇게 강제하면 createOrderItem 생성시 필요한 로직을 여기서만 관리할 수 있으니 유지보수가 편리하다
[중요 - 이렇게는 사용하지 말자]
OrderItem orderItem = new OrderItem();
orderItem.setItem(item);
orderItem.setCount(count);
}
*/
//==생성 메서드==//
public static OrderItem createOrderItem(Item item, int orderPrice, int count){
OrderItem orderItem = new OrderItem();
orderItem.setItem(item);
orderItem.setOrderPrice(orderPrice);
orderItem.setCount(count);
item.removeStock(count);
return orderItem;
}
//==비즈니스 로직==//
public void cancel(){
getItem().addStock(count);
}
//==조회 로직==//
/**
* 주문상품 전체 가격 조다
* @return
*/
public int getTotalPrice(){
return getOrderPrice() * getCount();
}
}
DeliveryStatus
package jpabook.jpashop.domain;
public enum DeliveryStatus {
READY, COMP
}
Item
package jpabook.jpashop.domain.item;
import jpabook.jpashop.domain.Category;
import jpabook.jpashop.exception.NotEnoughStockException;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
@Entity
@Getter
@Setter
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "dtype")
public abstract class Item {
@Id
@GeneratedValue
@Column(name = "item_id")
private Long id;
private String name;
private int price;
private int stockQuantity;
@ManyToMany(mappedBy = "items")
private List<Category> categories = new ArrayList<>();
//==비즈니스 로직==//
/**
* stock 증가
*/
public void addStock(int quantity){
this.stockQuantity += quantity;
}
/**
* stock 감소
*/
public void removeStock(int quantity){
int restStock = this.stockQuantity - quantity;
if(restStock < 0){
throw new NotEnoughStockException("need more stock");
}
this.stockQuantity = restStock;
}
}
Book
package jpabook.jpashop.domain.item;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
@Entity
@Getter @Setter
@DiscriminatorValue("D")
public class Book extends Item{
private String author;
private String isbn;
}
Movie
package jpabook.jpashop.domain.item;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
@Entity
@Getter @Setter
@DiscriminatorValue("M")
public class Movie extends Item{
private String artist;
private String etc;
}
Album
package jpabook.jpashop.domain.item;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
@Entity
@Getter @Setter
@DiscriminatorValue("A")
public class Album extends Item{
private String artist;
private String etc;
}
Category
package jpabook.jpashop.domain;
import jpabook.jpashop.domain.item.Item;
import lombok.Getter;
import lombok.Setter;
import org.springframework.context.annotation.EnableMBeanExport;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
@Entity
@Getter @Setter
public class Category {
@Id @GeneratedValue
@Column(name = "category_id")
private Long id;
private String name;
@ManyToMany
@JoinTable(name = "cateogry_item",
joinColumns = @JoinColumn(name = "category_id"),
inverseJoinColumns = @JoinColumn(name = "item_id"))
private List<Item> items = new ArrayList<>();
@ManyToOne
@JoinColumn(name = "parent_id")
private Category parent;
@OneToMany(mappedBy = "parent")
private List<Category> child = new ArrayList<>();
//연관관계 메서드
public void addChildCategory(Category child){
this.child.add(child);
child.setParent(this);
}
}
참고
댓글남기기