해결된 질문
작성
·
94
0
[질문 내용]
saveItem에 @Transactional 어노테이션이 선언되어있고, updateItemForm의 Id 값을 hidden으로 설정 되어있는데
상품 수정시 기존 값이 그대로 유지되면서 화면에 보여집니다ㅠㅠ
뭐가 잘못된걸까요...? 관련 코드도 댓글로 첨부 하겠습니다.
답변 3
0
안녕하세요. 다람님, 공식 서포터즈 y2gcoder입니다.
댓글로 첨부해주신 ItemController
의 updateItemForm()
을 확인해보시면,
@GetMapping("items/{itemId}/edit")
public String updateItemForm(@PathVariable("itemId") Long itemId, Model model) {
Book item = (Book) itemService.findOne(itemId);
BookForm form = new BookForm();
form.setName(item.getName());
form.setPrice(item.getPrice());
form.setStockQuantity(item.getStockQuantity());
form.setAuthor(item.getAuthor());
form.setIsbn(item.getIsbn());
model.addAttribute("bookForm", form);
return "items/updateItemForm";
}
bookForm
에는 id
값이 들어있지 않습니다!
그래서 updateItemForm.html
의 id
에는 null
값이 들어가게 됩니다.
그래서 updateItem()
에서 id
값이 null
로 들어간 Book
객체를 persist
하려고 하기 때문에 아마도 기존의 book
엔티티가 merge
에 의해 수정되는 것이 아닌, 새로운 book
엔티티가 persist
될 것 같습니다 🙂
한번 updateItem()
에서 브레이크 포인트를 걸어 디버깅해보시는 것도 좋을 것 같습니다 🙂
감사합니다.
0
ItemController
package jpabook.jpashop.controller;
import jakarta.validation.Valid;
import jpabook.jpashop.domain.Item.Book;
import jpabook.jpashop.domain.Item.Item;
import jpabook.jpashop.service.ItemService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import java.util.List;
@Controller
@RequiredArgsConstructor
public class ItemController {
private final ItemService itemService;
@GetMapping("/items/new")
public String createForm(Model model) {
model.addAttribute("bookForm", new BookForm());
return "items/createItemForm";
}
@PostMapping("/items/new")
public String create(@Valid BookForm form, BindingResult result) {
if (result.hasErrors()) {
System.out.println(result.getFieldError());
return "items/createItemForm";
}
Book book = new Book();
book.setName(form.getName());
book.setPrice(form.getPrice());
book.setStockQuantity(form.getStockQuantity());
book.setAuthor(form.getAuthor());
book.setIsbn(form.getIsbn());
itemService.saveItem(book);
return "redirect:/items";
}
@GetMapping("/items")
public String list(Model model) {
List<Item> items = itemService.findItems();
model.addAttribute("items", items);
return "items/itemList";
}
@GetMapping("items/{itemId}/edit")
public String updateItemForm(@PathVariable("itemId") Long itemId, Model model) {
Book item = (Book) itemService.findOne(itemId);
BookForm form = new BookForm();
form.setName(item.getName());
form.setPrice(item.getPrice());
form.setStockQuantity(item.getStockQuantity());
form.setAuthor(item.getAuthor());
form.setIsbn(item.getIsbn());
model.addAttribute("bookForm", form);
return "items/updateItemForm";
}
@PostMapping("/items/{itemId}/edit")
public String updateItem(@ModelAttribute("bookForm") BookForm form) {
Book book = new Book();
book.setId(form.getId());
book.setName(form.getName());
book.setPrice(form.getPrice());
book.setStockQuantity(form.getStockQuantity());
book.setAuthor(form.getAuthor());
book.setIsbn(form.getIsbn());
itemService.saveItem(book);
return "redirect:/items";
}
}
ItemService
package jpabook.jpashop.repository;
import jakarta.persistence.EntityManager;
import jakarta.persistence.TypedQuery;
import jakarta.persistence.criteria.*;
import jpabook.jpashop.domain.Member;
import lombok.RequiredArgsConstructor;
import jpabook.jpashop.domain.Order;
import org.springframework.data.web.config.EnableSpringDataWebSupport;
import org.springframework.stereotype.Repository;
import org.springframework.util.StringUtils;
import java.util.ArrayList;
import java.util.List;
@Repository
@RequiredArgsConstructor
public class OrderRepository {
private final EntityManager em;
public void save(Order order) {
em.persist(order);
}
public Order findOne(Long id) {
return em.find(Order.class, id);
}
/*
JPQL
*/
public List<Order> findAll(OrderSearch orderSearch) {
//language=JPAQL
String jpql = "select o From Order o join o.member m";
boolean isFirstCondition = true;
//주문 상태 검색
if (orderSearch.getOrderStatus() != null) {
if (isFirstCondition) {
jpql += " where";
isFirstCondition = false;
} else {
jpql += " and";
}
jpql += " o.status = :status";
}
//회원 이름 검색
if (StringUtils.hasText(orderSearch.getMemberName())) {
if (isFirstCondition) {
jpql += " where";
isFirstCondition = false;
} else {
jpql += " and";
}
jpql += " m.name like :name";
}
TypedQuery<Order> query = em.createQuery(jpql, Order.class) .setMaxResults(1000); //최대 1000건
if (orderSearch.getOrderStatus() != null) {
query = query.setParameter("status", orderSearch.getOrderStatus());
}
if (StringUtils.hasText(orderSearch.getMemberName())) {
query = query.setParameter("name", orderSearch.getMemberName());
}
return query.getResultList();
}
/*
JPA Criteria
*/
public List<Order> findAllByCriteria(OrderSearch orderSearch) {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Order> cq = cb.createQuery(Order.class);
Root<Order> o = cq.from(Order.class);
Join<Order, Member> m = o.join("member", JoinType.INNER); //회원과 조인
List<Predicate> criteria = new ArrayList<>();
//주문 상태 검색
if (orderSearch.getOrderStatus() != null) {
Predicate status = cb.equal(o.get("status"),
orderSearch.getOrderStatus());
criteria.add(status);
}
//회원 이름 검색
if (StringUtils.hasText(orderSearch.getMemberName())) {
Predicate name =
cb.like(m.<String>get("name"), "%" + orderSearch.getMemberName()
+ "%");
criteria.add(name);
}
cq.where(cb.and(criteria.toArray(new Predicate[criteria.size()])));
TypedQuery<Order> query = em.createQuery(cq).setMaxResults(1000); //최대 1000 건
return query.getResultList();
}
}
ItemRepository
package jpabook.jpashop.repository;
import jakarta.persistence.EntityManager;
import jpabook.jpashop.domain.Item.Item;
import lombok.RequiredArgsConstructor;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
@RequiredArgsConstructor
public class ItemRepository {
private final EntityManager em;
public void save(Item item) {
if (item.getId() == null) {
em.persist(item);
} else {
em.merge(item);
}
}
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();
}
}
updateItemForm.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head th:replace="fragments/header :: header" />
<body>
<div class="container">
<div th:replace="fragments/bodyHeader :: bodyHeader"/>
<form th:object="${bookForm}" method="post">
<!-- id -->
<input type="hidden" th:field="*{id}" />
<div class="form-group">
<label th:for="name">상품명</label>
<input type="text" th:field="*{name}" class="form-control"
placeholder="이름을 입력하세요" /> </div>
<div class="form-group">
<label th:for="price">가격</label>
<input type="number" th:field="*{price}" class="form-control"
placeholder="가격을 입력하세요" /> </div>
<div class="form-group">
<label th:for="stockQuantity">수량</label>
<input type="number" th:field="*{stockQuantity}" class="form-control" placeholder="수량을 입력하세요" /> </div>
<div class="form-group">
<label th:for="author">저자</label>
<input type="text" th:field="*{author}" class="form-control"
placeholder="저자를 입력하세요" /> </div>
<div class="form-group">
<label th:for="isbn">ISBN</label>
<input type="text" th:field="*{isbn}" class="form-control"
placeholder="ISBN을 입력하세요" /> </div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
<div th:replace="fragments/footer :: footer" />
</div> <!-- /container -->
</body>
</html>
0
Item
package jpabook.jpashop.domain.Item;
import jakarta.persistence.*;
import jpabook.jpashop.domain.Category;
import jpabook.jpashop.exception.NotEnoughStockException;
import lombok.Getter;
import lombok.Setter;
import java.util.ArrayList;
import java.util.List;
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "dtype")
@Getter @Setter
public 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 jakarta.persistence.DiscriminatorValue;
import jakarta.persistence.Entity;
import lombok.Getter;
import lombok.Setter;
@Entity
@DiscriminatorValue("B")
@Getter @Setter
public class Book extends Item {
private String author;
private String isbn;
}
BookForm
package jpabook.jpashop.controller;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Getter;
import lombok.Setter;
@Getter @Setter
public class BookForm {
private Long id;
@NotEmpty(message = "상품명은 필수 입니다")
private String name;
@NotNull(message = "가격은 필수입니다.")
private Integer price;
@NotNull(message = "수량은 필수입니다.")
private Integer stockQuantity;
private String author;
private String isbn;
}
어제는 아무리 봐도 안보였는데....
오늘 보니 빠진게 또 보이네요...ㅎ휴ㅠㅠㅠ
타이핑하면서 따라가다보니 놓찬 것 같습니다ㅠㅠ
추가 전/후로 디버깅하면서 persist, merge 차이도 다시 보고 하겠습니다!!!
답변해주셔서 감사합니다!!!ㅎㅎ