1. 아이템 수정
1-1. 준영속 상태에서의 아이템 수정
- 변경 감지 기능 사용
- 병합(merge) 사용 -> 사용 금지
@Repository
@RequiredArgsConstructor
public class ItemRepository {
private final EntityManager em;
public void save(Item item) {
em.persist(item);
}
@PostMapping("/items/{itemId}/edit")
public String updateItem(@ModelAttribute("form")Item form) {
Item item = new Item();
item.setId(form.getId());
item.setName(form.getName());
item.setPrice(form.getPrice());
item.setStockQuantity(form.getStockQuantity());
itemService.updateItem(form.getId(), item);
return "redirect:/items";
}
1-2. merge의 문제점
- 변경을 원치 않는 값의 경우 내가 set을 해주지 않으면 해당 값이 null로 update가 된다.
- 필요한 값만 update를 원한다면 변경감지를 해주어야 한다.
- 즉, merge는 실무에서 절대 쓰지 않는 사용법
1-3. 준영속과 비영속의 차이점
@Transactional
public Item updateItem(Long itemId, Item item) {
Item findItem = itemRepository.findOne(itemId);
findItem.setPrice(item.getPrice());
findItem.setName(item.getName());
findItem.setStockQuantity(item.getStockQuantity());
return findItem;
}
💻 update 유지보수
@PostMapping("/items/{itemId}/edit")
public String updateItem(@PathVariable Long itemId,
@ModelAttribute("form")Item form) {
itemService.updateItem(itemId,
form.getName(), form.getPrice(), form.getStockQuantity());
return "redirect:/items";
}
public void updateItem(Long itemId, String name, int price, int stockQuantity) {
Item findItem = itemRepository.findOne(itemId);
findItem.setName(name);
findItem.setPrice(price);
findItem.setStockQuantity(stockQuantity);
}
1-4. 엔티티 변경시에는 항상 변경감지를 이용
- 컨트롤러 단에서 엔티티를 생성하기 보단, 트랜잭션이 있는 서비스 계층에 식별자(id)와 변경할 데이터(파라미터 or DTO)를 명확하게 전달.
- 트랜잭션이 있는 서비스 계층에서 영속 상태의 엔티티를 조회하고, 엔티티의 데이터를 직접 변경
- 트랜잭션 커밋시점에 변경감지가 실행된다.
💻 상품주문 페이지
@Controller
@RequiredArgsConstructor
public class OrderController {
private final OrderService orderService;
private final MemberService memberService;
private final ItemService itemService;
@GetMapping("/order")
public String createForm(Model model) {
List<Member> members = memberService.findMembers();
model.addAttribute("members", members);
List<Item> items = itemService.findItems();
model.addAttribute("items", items);
return "order/orderForm";
}
@PostMapping("/orders")
public String order(@RequestParam("memberId") Long memberId,
@RequestParam("itemId") Long itemId,
@RequestParam("count") int count) {
orderService.order(memberId, itemId, count);
return "redirect:/order";
}
@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class OrderService {
private final OrderRepository orderRepository;
private final MemberRepository memberRepository;
private final ItemRepository itemRepository;
@Transactional
public Long order(Long memberId, Long itemId, int count) {
Member member = memberRepository.findOne(memberId);
Item item = itemRepository.findOne(itemId);
OrderItem orderItem =
OrderItem.createOrderItem(item,item.getPrice(), count);
Order order = Order.createOrder(member, orderItem);
orderRepository.save(order);
return order.getId();
}
@Repository
@RequiredArgsConstructor
public class OrderRepository {
private final EntityManager em;
public void save(Order order) {
em.persist(order);
}
}
@Entity
@Getter @Setter
@Table(name = "orders")
public class Order {
@Id @GeneratedValue
@Column(name = "order_id")
private Long id;
private LocalDateTime orderDate;
@Enumerated(EnumType.STRING)
private OrderStatus status;
@OneToMany(mappedBy = "order")
private List<OrderItem> orderItems = new ArrayList<>();
@ManyToOne
@JoinColumn(name = "member_id")
private Member member;
public void setMember(Member member) {
this.member = member;
member.getOrders().add(this);
}
public void addOrderItem(OrderItem orderItem) {
orderItems.add(orderItem);
orderItem.setOrder(this);
}
public static Order createOrder(Member member, OrderItem... orderItems) {
Order order = new Order();
order.setMember(member);
for( OrderItem orderItem : orderItems ) {
order.addOrderItem(orderItem);
}
order.setStatus(OrderStatus.ORDER);
order.setOrderDate(LocalDateTime.now());
return order;
}
}
@Entity
@Getter @Setter
public class OrderItem {
@Id @GeneratedValue
@Column(name = "order_item_id")
private Long id;
private int orderPrice;
private int count;
@ManyToOne
@JoinColumn(name = "order_id")
private Order order;
@ManyToOne
@JoinColumn(name = "item_id")
private Item item;
public static OrderItem createOrderItem(Item item, int price, int count) {
OrderItem orderItem = new OrderItem();
orderItem.setItem(item);
orderItem.setOrderPrice(price);;
orderItem.setCount(count);
item.removeStock(count);
return orderItem;
}
}
<div class="container">
<div th:replace="fragments/bodyHeader :: bodyHeader" />
<form role="form" action="/orders" method="post">
<div class="form-group">
<label for="member">주문회원</label>
<select name="memberId" id="member" class="form-control">
<option value="">회원선택</option>
<option th:each="member : ${members}"
th:value="${member.id}"
th:text="${member.name}"
/>
</select>
</div>
<div class="form-group">
<label for="item">상품명</label>
<select name="itemId" id="item" class="form-control">
<option value="">상품선택</option>
<option th:each="item : ${items}"
th:value="${item.id}"
th:text="${item.name}"
/>
</select>
</div>
<div class="form-group">
<label for="count">주문수량</label>
<input type="number" name="count" class="form-control"
id="count" placeholder="주문 수량을 입력하세요">
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
<br />
<div th:replace="fragments/footer :: footer" />
</div>