🧩 아이템 수정 - 변경감지와 머지(merge)
준영속(detached)
- 영속성 컨텍스트에 저장되었다가 분리된 상태로 현재는 영속상태가 아닌 상태이다.
 
- 영속성 컨텍스트가 제공하는 기능을 사용하지 못한다.
 
준영속 엔티티를 수정하는 2가지 방법
- 변경 감지 기능 사용 ( 권장 )
 
- 병합(merge)사용 (권장 X)
 
* 준영속과 비영속의 차이점
- 준영속과 비영속의 차이는 바로 영속상태가 되어본 경험이다.
 
- 영속상태가 되기 위해서는 식별자 반드시 필요하다. 
 
-  준영속 상태의 엔티티는 식별자가 존재한다.
 
- 비영속 상태의 엔티티는 식별자가 존재할수도, 존재하지 않을 수도 있다.
 
@PostMapping("/items/{itemId}/edit")
	public String updateItem(@PathVariable Long itemId, @ModelAttribute("form") Item form) {
		
		Item item = new Item();
		item.setId(item.getId());
		item.setName(item.getName());
		item.setPrice(item.getPrice());
		item.setStockQuantity(item.getStockQuantity());
        
		
		itemService.updateItem(itemId,form.getName(),form.getPrice(),form.getStockQuantity());
			return "redirect:/items";
	}
- 새로 생성 된 item객체는 new를 통해 새로 생성된 객체이고, 아직 영속성 컨텍스트에는 등록되지 않았지만, 식별자를 가지고 있다. 
 
- 이처럼 임의로 엔티티 객체를 만들어 내도 식별자를 가지고 있으면 준영속 엔티티라 한다.
 
변경감지(영속 상태 만들기)
- 영속성 컨텍스트에 속해있으려면 해당 데이터를 select해준다.
 
- select문을 통해서 영속성 컨텍스트에서 가져올건데, 영속성 컨텍스트에 데이터가 없으면 DB에서 가져온다.
 
- select 요청을 함으로써, 영속성 컨텍스트영역으로 요청을 보내서 그 안에 내 정보가 담기게된다.
 
- findItem 이라는 객체는 영속 상태에 놓이게 된다.
Item findItem = itemRepository.findOne(itemid); 
	@Transactional
	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);
	}
-  준영속 상태인 객체를 영속상태로 바꾸고 그 영속상태인 데이터를 가지고 
set만을 해주면 변경감지를 통해 update가 수행된다. 
Transaction이 종료되는순간 자동으로 변경된 부분을 감지해서 update를 수행한다.
이처럼 변경점을 감지해서 update하는 것을 변경 감지라 한다.
 
🧩 Domain
@Entity
@Getter @Setter
public class Item {
	@Id @GeneratedValue
	@Column(name = "item_id")
	private Long id;
	
	private String name;
	private int price;
	private int stockQuantity;
	
	
	
	public void removeStock(int quantity) {
		int restStock = this.stockQuantity - quantity;
		
		if(restStock < 0) {
			
			throw new NotEnoughStockException("need more stock");
		}
		this.stockQuantity = restStock;
	}
	
	
	public void addStock(int quantity) {
		this.stockQuantity += quantity;
	}
}
🧩 DTO
@Getter @Setter
public class ItemForm {
	
	private Long id;
	private String name;
	private int price;
	private int stockQuantity;
	
	
}
🧩 Web
<!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:action="@{/items/new}"  th:object="${itemForm}" method="post">
			<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>
			<button type="submit" class="btn btn-primary">Submit</button>
		</form>
		<br />
		<div th:replace="fragments/footer :: footer" />
	</div>
	
</body>
</html>
itemList
<!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" />
		<div>
			<table class="table table-striped">
				<thead>
					<tr>
						<th>#</th>
						<th>상품명</th>
						<th>가격</th>
						<th>재고수량</th>
						<th></th>
					</tr>
				</thead>
				<tbody>
					<tr th:each="item : ${items}">
						<td th:text="${item.id}"></td>
						<td th:text="${item.name}"></td>
						<td th:text="${item.price}"></td>
						<td th:text="${item.stockQuantity}"></td>
						<td>
							<a th:href="@{/items/{id}/edit(id=${item.id})}"
								class="btn btn-primary" role="button">수정</a> 
						</td>
					</tr>
				</tbody>
			</table>
		</div>
		
		<div th:replace="fragments/footer :: footer" />
	</div>
	
</body>
</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="${form}" method="post">
			<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>
			<button type="submit" class="btn btn-primary">Submit</button>
		</form>
		<div th:replace="fragments/footer :: footer" />
	</div>
	
</body>
</html>
🧩 Controller
@Controller
@RequiredArgsConstructor
public class ItemController {
	
	private final ItemService itemService;
	
	
	
	@GetMapping(value = "items/new")
	public String createForm(Model model) {
		model.addAttribute("itemForm", new ItemForm());
		return "items/createItemForm";
	}
	
	
	
	@PostMapping("/items/new")
	public String create(ItemForm itemform) {
		Item item = new Item();
		item.setName(itemform.getName());
		item.setPrice(itemform.getPrice());
		item.setStockQuantity(itemform.getStockQuantity());
		
		itemService.saveItem(item);
		
		return "redirect:/";
	}
	
	
	
	@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){
        Item item = itemService.findOne(itemId);
        ItemForm form = new ItemForm();
        form.setId(item.getId());
        form.setName(item.getName());
        form.setPrice(item.getPrice());
        form.setStockQuantity(item.getStockQuantity());
        
        model.addAttribute("form", form);
        return "items/updateItemForm";
    }
	
	@PostMapping("/items/{itemId}/edit")
	public String updateItem(@PathVariable Long itemId, @ModelAttribute("form") Item form) {
		
		Item item = new Item();
		item.setId(item.getId());
		item.setName(item.getName());
		item.setPrice(item.getPrice());
		item.setStockQuantity(item.getStockQuantity());
        
		
		itemService.updateItem(itemId,form.getName(),form.getPrice(),form.getStockQuantity());
		return "redirect:/items";
	}
}
🧩 Service
ItemService
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
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);
	}
	
	@Transactional
	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);
	}
}
🧩 Repository
ItemRepository
@Repository
@RequiredArgsConstructor
public class ItemRepository {
	
	@Autowired
	private final EntityManager em;
	
	public void save(Item item) {
		
			
			
		
		em.persist(item);
	}
	public List<Item> findAll() {
		return em.createQuery("select i from Item i", Item.class).getResultList();
	}
	public Item findOne(Long id) {
		return em.find(Item.class, id);
	}
	
}