//상품목록수정
@PostMapping(value = "/items/{itemId}/edit")
public String updateItem(@ModelAttribute("form")BookForm form) {
log.info("updateItem itemId->{}",form.getId());
log.info("updateItem getName->{}",form.getName());
// 1. [controller-->Service ]-->updateItem(id, name , price)
// 2. [Service -->Repository ]--> 수정
//방법2
//itemService.updateItem(form.getId(), form.getName(),form.getPrice());
itemService.updateItem(form);
return "redirect:/items";
//상품수정
public void updateItem(BookForm form) {
Item item = itemRepository.findOne(form.getId());
item.setName(form.getName());
item.setPrice(form.getPrice());
}
@Transactional (readOnly = true)??
조회한 데이터를 return 한다고 해도 의도치 않게 데이터가 변경되는 일을 사전에 방지해줍니다. (성능향상)
package com.oracle.oBootJpa03.controller;
import java.util.List;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import com.oracle.oBootJpa03.domain.Member;
import com.oracle.oBootJpa03.domain.item.Item;
import com.oracle.oBootJpa03.service.ItemService;
import com.oracle.oBootJpa03.service.MemberService;
import com.oracle.oBootJpa03.service.OrderService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Controller
@Slf4j
@RequiredArgsConstructor
public class OrderController {
private final OrderService orderService;
private final MemberService memberService;
private final ItemService itemService;
// 회원, 아이템 불러오기
@GetMapping(value = "/order")
public String createOrderForm(Model model) {
log.info("OrderController createOrderForm Start...");
List<Member> members = memberService.findMembers();
List<Item> items = itemService.findItems();
model.addAttribute("members", members);
model.addAttribute("items", items);
return "order/orderForm";
}
// OrderController -> orderSave
}
package com.oracle.oBootJpa03.service;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.oracle.oBootJpa03.repository.ItemRepository;
import com.oracle.oBootJpa03.repository.MemeberRepository;
import com.oracle.oBootJpa03.repository.OrderRepository;
import lombok.RequiredArgsConstructor;
@Service
@Transactional
@RequiredArgsConstructor //생성자 자동생성
public class OrderService {
private final OrderRepository orderRepository;
private final MemeberRepository memeberRepository;
private final ItemRepository itemRepository;
}
package com.oracle.oBootJpa03.repository;
import javax.persistence.EntityManager;
import org.springframework.stereotype.Repository;
import lombok.RequiredArgsConstructor;
@Repository
@RequiredArgsConstructor //생성자 자동생성
public class OrderRepository {
private final EntityManager em;
}
🔽밑 표 보고 OrderItem 작성하기
//객체명 : order_item_seq
//DB SEQ : order_item_sequence
//Table : order_item
package com.oracle.oBootJpa03.domain.item;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import org.springframework.data.mapping.AccessOptions.GetOptions.GetNulls;
import com.oracle.oBootJpa03.domain.Order;
import lombok.Data;
//객체명 : order_item_seq
//DB SEQ : order_item_sequence
//Table : order_item
@Entity
@Data
@SequenceGenerator(name = "order_item_seq" ,
sequenceName = "order_item_sequence",
initialValue = 1,
allocationSize = 1
)
@Table(name = "order_item")
public class OrderItem {
protected OrderItem() {
}
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE,
generator = "order_item_seq"
)
@Column(name = "order_item_id")
private Long id;
@ManyToOne
@JoinColumn(name = "item_id")
private Item item; //주문상품
@ManyToOne
@JoinColumn(name = "order_id")
private Order order; //주문
private int orderPrice; //주문가격
private int count; //주문수량
//생성 메서드( createOrderItem() ): 주문 상품, 가격, 수량 정보를 사용해서 주문상품 엔티티를 생성
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;
}
//상품주문
@PostMapping(value = "/orderSave")
public String orderSave(@RequestParam("memberId") Long memberId,
@RequestParam("itemId") Long itemId,
@RequestParam("count") int count ) {
log.info("Order orderSave Start...");
orderService.order(memberId, itemId, count);
return "redirect:/";
}
public Member findOne(Long id) {
// 원래방법 Member member = em.find(Member.class, id);
// return member;
//한번에 보내기
return em.find(Member.class, id);
추가 코딩
public static Order createOrder(Member member, Delivery delivery ) {
Order order = new Order();
order.setMember(member);
order.setDelivery(delivery);
order.setStatus(OrderStatus.ORDER);
order.setOrderDate(LocalDateTime.now());
return order;
}
package com.oracle.oBootJpa03.service;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.oracle.oBootJpa03.domain.Delivery;
import com.oracle.oBootJpa03.domain.DeliveryStatus;
import com.oracle.oBootJpa03.domain.Member;
import com.oracle.oBootJpa03.domain.Order;
import com.oracle.oBootJpa03.domain.item.Item;
import com.oracle.oBootJpa03.domain.item.OrderItem;
import com.oracle.oBootJpa03.repository.ItemRepository;
import com.oracle.oBootJpa03.repository.MemeberRepository;
import com.oracle.oBootJpa03.repository.OrderRepository;
import lombok.RequiredArgsConstructor;
@Service
@Transactional
@RequiredArgsConstructor //생성자 자동생성
public class OrderService {
private final OrderRepository orderRepository;
private final MemeberRepository memeberRepository;
private final ItemRepository itemRepository;
//1. Delivery Save
//2. Order Save
//3. OrderItem Save
//4. 재고 --> X, OrderItem은 하나만 처리
public void order(Long memberId, Long itemId, int count) {
//엔티티 조회
Member member = memeberRepository.findOne(memberId);
Item item = itemRepository.findOne(itemId);
//1. 주문(배송)정보 생성(Member 주소 배송 가정)
Delivery delivery = new Delivery();
delivery.setAddress(member.getAddress());
delivery.setStatus(DeliveryStatus.READY);
//주문(배송)정보 저장
orderRepository.saveDelivery(delivery);
System.out.println(" OrderService delivery.getId()-> "+delivery.getId());
System.out.println(" OrderService item.getId()-> "+item.getId());
System.out.println(" OrderService member.getId()-> "+member.getId());
// 2- 1,주문생성
Order order= Order.createOrder(member, delivery);
//2-2 주문저장
orderRepository.saveOrder(order);
//3. 주문상품 생성
//OrderItem orederItem - new OrderItem(); --연관관계 Mapping 처리 위해
OrderItem orderItem = OrderItem.createOrderItem(item, item.getPrice(), count);
//전시용,위에다편입
orderItem.setOrder(order);
orderRepository.saveOrderItem(orderItem);
}
}
package com.oracle.oBootJpa03.repository;
import javax.persistence.EntityManager;
import org.springframework.stereotype.Repository;
import com.oracle.oBootJpa03.domain.Delivery;
import com.oracle.oBootJpa03.domain.Order;
import com.oracle.oBootJpa03.domain.item.OrderItem;
import lombok.RequiredArgsConstructor;
@Repository
@RequiredArgsConstructor //생성자 자동생성
public class OrderRepository {
private final EntityManager em;
//주문(배송)정보 저장
public void saveDelivery(Delivery delivery) {
em.persist(delivery);
}
//2-2 주문저장
public void saveOrder(Order order) {
em.persist(order);
}
//3. 주문상품 생성
public void saveOrderItem(OrderItem orderItem) {
em.persist(orderItem);
}
}
- Controller
method-->orderList (OrderSearch, Model)- Controller--> Service
method-->findOrders();- Service --> Repository
method-->findAll();
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<div class="container">
<table border="1">
<thead>
<tr>
<th>#</th>
<th>회원명</th>
<th>배송지</th>
<th>상태</th>
<th>일시</th>
</tr>
</thead>
<tbody>
<tr th:each="order : ${orders}" >
<td th:text="${order.id}"></td>
<td th:text="${order.member.name}"></td>
<td th:text="${order.delivery.address.city}"></td>
<td th:text="${order.status}"></td>
<td th:text="${order.orderDate}"></td>
</tr>
</tbody>
</table>
</div>
</body>
</html>
//주문내역
@GetMapping(value = "/orders")
public String orderList( OrderSearch orderSearch ,Model model) {
System.out.println("OrderController orderList . . . ");
List<Order> orders = orderService.findOrders();
model.addAttribute("orders", orders);
return "order/orderList";
}
//주문내역 목록보기
public List<Order> findOrders() {
List<Order> findorderList = orderRepository.findAll();
System.out.println("OrderRepository findorderList.size()->"+findorderList.size());
return findorderList;
}
//주문내역 목록보기
public List<Order> findAll() {
return em.createQuery("select o from Order o", Order.class)
.getResultList();
}
실제 @ManyToOne의 경우 FK쪽의 엔티티를 가져올 때 PK쪽의 엔티티도 같이 가져오게 되는데요, 이러한 과정이 꼭 필요한건지, 필요하지 않다면 어떻게 해결할 수 있는지
- 특정 엔티티를 조회할 때 연관된 모든 엔티티를 같이 로딩하는 것을 즉시 로딩(Eager Loading)이라고 합니다
- 지연 로딩(Lazy Loading)이란, 가능한 객체의 초기화를 지연시키는데 사용하는 패턴입니다.
- 이와 같은 즉시 로딩은 연관된 엔티티를 모두 가져온다는 장점이 있지만,
실무에서 엔티티간의 관계가 복잡해질수록 조인으로 인한 성능 저하를 피할 수 없게 됩니다.- '즉시 로딩'은 불필요한 조인까지 포함해서 처리하는 경우가 많기 때문에 '지연 로딩'의 사용을 권장하고 있습니다.
- (보편적으로 '지연 로딩'을 기본으로 사용하고, 상황에 맞게 필요한 방법을 찾는것이 좋은 것 같습니다 😃)
- 재연결이 필요한데, @Transactional 어노테이션을 통해 해결할 수 있습니다.