(구현 기능): ¹상품 등록 ²상품 목록 조회 ³상품 수정
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE) //한테이블에 떄려박는 느낌으로.
@DiscriminatorColumn(name = "dtype") //SINGLE_TABLE 아래의 타입들을 찾고자
@Getter @Setter
//@Setter -> 핵심 비즈니스의 값을 변경할때 굳이 필요 없이 필요한 곳에서 메서드를 만들어야 함
//구현체를 가지기 위해- 추상클래스
public abstract class Item {
@Id
@GeneratedValue
@Column(name = "item_id") //pk 설정
private Long id;
.
.
.
private int stockQuantity; //재고수량
//--비즈니스 로직--/
//왜 이곳에?
//객체지향적으로 이 객체를 가지고 있는 쪽에서 메서드를 만드는 것이 가장 좋기 때문
/*
* 재고수량 증가 (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;
}
}
[이 강아지의 코딩공부, Gemini Kim블로그에서 setter를 언제 쓰면 좋을지 참고하였습니다!]
왜 @Setter
을 굳이 지양하고 비즈니스 로직을 해당 클레스에서 만들어야 할까?
📣) @Setter의 지양
jpa는 트랜젝션 안에서 Entity의 변경사항을 감지해 UPDATE SQL을 생성한다.
=> @Setter로 인해 UPDATE 기능이 수행되고 있었다.
=> 예측하지 못한 곳에서 update가 일어나면 일일히 Setter을 확인하는 불편이 생길 수 있다.
이러한 여러가지 관점으로 인해 이렇게 setter
을 대신해 메서드를 이용하여, 관련된 해당 클래스에 비즈니스 로직을 생성해주는 것이 좋다. 굳이 다른 클래스에서 setter
를 이용해 값을 하나하나 불러와 변경할 필요가 없게 된다.
@Repository
@RequiredArgsConstructor
public class ItemRepository {
//스프링 데이터 jpa땜에
private final EntityManager em;
//*상품저장* item은 맨처음엔 id가 없음
public void save(Item item){
//맨 처음에는 id값이 없다 == 새로 생성한 객체
if(item.getId() == null){
em.persist(item); //신규로 등록하기
}else{//이미 등록되어 있는 것->DB에서 가져와서 저장된 엔티티를 수정한다 생각하자.
//자세한 것은 웹 뒤에서
em.merge(item);
}
}
public Item findOne(Long id){
return em.find(Item.class, id); //단건 조회니까 find() 쓰자
}
public List<Item> findAll(){
return em.createQuery("select i from Item i", Item.class)
.getResultList();
}
}
영속성 컨텍스트와 JPA에 관하여 이 Namjun Kim 블로그를 참조하였습니다.
id
는 값이 없으므로 save()
메서드에서는 id값을 확인 후 em.persist()
로 최초 생성된 엔티티를 영속화 한다.em.merge()
를 써준다. 이를 생각해보면 객체가 이미 등록되어 있다.@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class ItemService {
private final ItemRepository itemRepository;
@Transactional //위에 (readOnly)때문에 읽기전용이면 저장이 안됨.->쓰기로 바꿔줌
public void saveItem(Item item){
itemRepository.save(item);
}
public List<Item> findItems(){
return itemRepository.findAll();
}
public Item findOne(Long itemId){
return itemRepository.findOne(itemId);
}
}
상품 서비스는 단순히 상품 리포지토리에 위임한게 다이다.
✔ @Transactional(readOnly=true)
로 되어 있기 때문에 저장이 필요한 saveItem()
메서드만 따로 '읽기전용'에서 '쓰기전용'으로 바꿔주었다.
테스트는 회원 테스트와 비슷하므로 건너뛴다.