1. 상품 엔티티 개발
@Entity
@Inheritance( strategy = InheritanceType.SINGLE_TABLE )
@DiscriminatorColumn( name = "dtype" )
@Getter @Setter
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<Category>();
// 비즈니스 로직
public void addStockQuantity(int quantity) { // 재고 수량 증가
this.stockQuantity += quantity;
}
public void removeStockQuantity(int quantity) { // 재고 수량 감소
int left = this.stockQuantity - quantity;
if (left < 0)
throw new NotEnoughStockException("need more stock");
this.stockQuantity = left;
}
}
- 이전에 이미 개발한 Item에 비즈니스 로직을 추가해줬다.
- 생각해보면 Member를 다룰 때에는 필드만 추가했었기 때문에 Item 클래스에 직접 비즈니스 로직을 추가하는 것이 어색했다.
- 그런데 Member는 필드 값을 직접 바꾸는 일이 없었고, Item은 quantity를 매번 바꿔야 한다는 차이가 있었다.
- 강의에서도 객체지향을 유지하기 위해서는 Item에서 메서드를 지니는 게 응집력을 높인다고 한다.
- 그래서 quantity를 늘리고 줄이는 메서드를 각각 넣었다.
- 그런데 줄이는 경우에는 quantity가 0보다 작아지는 경우를 따져서 예외를 일으켜야 한다.
- 그래서 NotEnoughStockException을 새롭게 정의하여 사용했다.
public class NotEnoughStockException extends RuntimeException {
public NotEnoughStockException() {
super();
}
public NotEnoughStockException(String message) {
super(message);
}
public NotEnoughStockException(String message, Throwable cause) {
super(message, cause);
}
public NotEnoughStockException(Throwable cause) {
super(cause);
}
}
2. 상품 리포지토리 개발
@Repository
@RequiredArgsConstructor
public class ItemRepository {
private final EntityManager em;
public void save(Item item) {
if (item.getId() == null) // persist() 이전에 item 인스턴스에는 id가 없기 때문에 if문으로 새로운 item이라는 보장을 받을 수 있다.
em.persist(item); // 새로운 객체로 등록한다.
else
em.merge(item); // 이미 DB에 있고, update하는 것
}
public Item findOne(Long id) {
return em.find(Item.class, id);
}
public List<Item> findAll() {
return em.createQuery("select i from Item i", Item.class)
.getResultList();
}
}
- 역시나 @Repository로 스프링한테 리포지토리 역할을 하는 빈으로 등록해달라고 요청한다.
- 역시나 @RequiredArgsConstructor로 final이 붙은 EntityManager를 주입 받아 사용한다.
- save()를 살펴 보면 if (item.getId() == null)을 사용하고 있다.
- 이는 이 아이템이 DB에 이미 등록되어 있는 객체인지 확인하는 것이다.
- 되어 있지 않으면 persist()를 쓰지만, 이미 있는 Item은 업데이트의 개념을 가진 merge()를 호출한다.
3. 상품 서비스 개발
@Service
@Transactional( readOnly = true )
@RequiredArgsConstructor
public class ItemService {
private final ItemRepository itemRepository;
@Transactional
public void saveItem(Item item) {
itemRepository.save(item);
}
public List<Item> findItems() {
return itemRepository.findAll();
}
public Item findOne(Long itemId) {
return itemRepository.findOne(itemId);
}
}
- 역시나 @Service로 스프링한테 서비스 역할을 하는 빈으로 등록해달라고 요청한다.
- 역시나 @Transactional로 DB 접근 메서드들을 트랜잭션 안에 두되, 읽기 전용 메서드가 더 많아서 readOnly = false로 둔다.
- 역시나 @RequiredArgsConstructor로 ItemRepository 객체를 가져와서 쓴다.
- 전체적으로 ItemRepository의 기능을 그대로 가져와서 쓴다. ( 다만, ItemService가 서비스 계층이므로 더 바깥에 있다. )
- saveItem()은 아이템을 DB에 저장해서 DB 내용을 바꾸기 때문에 @Transactional을 따로 둔다.
- 참고로 @Transactional의 readOnly 디폴트 값은 false다.
'스프링 (인프런) > 스프링부트' 카테고리의 다른 글
웹 계층 개발 (0) | 2023.07.08 |
---|---|
주문 도메인 개발 (0) | 2023.07.07 |
애플리케이션 구현 준비 (0) | 2023.07.05 |
엔티티 클래스 개발 (0) | 2023.07.05 |
도메인 모델과 테이블 설계 (0) | 2023.07.03 |