최대 절전 모드 - merge()를 피해야 하는 이유
기본 엔터티를 사용한다고 가정해 보겠습니다.
@Entity
@Table(name = product)
public class Product {
@Id
private int id;
private int stock;
}
적용할 시나리오는 제품의 재고 수를 줄이는 것입니다. 먼저 제품을 찾고 1씩 줄이는 구현 디자인을 생각해 냈습니다. 마지막으로 성공하면 제품 수를 확인합니다.
@Test
void testDecreaseStock() {
// Let's say the product id is 1
Product oldProduct = productService.find(1);
oldProduct.decrementStock(oldProduct);
Product newProduct = productService.find(1);
Logger.info("Product stock is: ", newProduct.getStock() )
}
제품 서비스에 대한 세부 정보는 다음과 같습니다.
@Transactional
public Product find(int id) {
return entityManager.find(Product.class, id);
}
@Transactional
public void decrementStock (Product p) {
p.setStock(p.getStock() - 1);
entityManager.merge(p);
}
이것이 find() 및 decrementStock() 메소드의 가장 기본적인 구현이므로 혼란스럽지 않기를 바랍니다. 따라서 모든 것이 명확하고 코드를 실행했습니다. 그러나 3개 대신 4개 상태가 실행되는 것을 확인했습니다. 어떻게?
예상되는:
Product oldProduct = productService.find(1);
1oldProduct.decrementStock(oldProduct);
2Product newProduct = productService.find(1);
삼실제:
select ... from product where id=1
1select ... from product where id=1
2update product set stock where id=1
삼select ... from product where id=1
4두 번째로 추가 선택 쿼리가 있는 이유는 무엇입니까?
우리가 무엇을 하든(삽입, 업데이트, 삭제) Hibernate는 먼저 그것을 파악하기를 원합니다. . 데이터베이스의 스냅샷을 로드하고 기본적으로 "현재 데이터베이스에 무엇이 있습니까?"를 파악하기 위해 select 문을 실행합니다. 추가 선택 쿼리를 보셨나요? 이것이 출력에 2행이 있는 이유입니다.
고쳐보자 ✔️
해결책은 제품의 ID를 decrementStock()에 전달하고 merge()를 제거하는 것입니다. 이미 "관리"단계에 있기 때문입니다. 트랜잭션은 decrementStock() 메서드에서 계속됩니다. 그러나 제품을 찾아 이를 decrementStock()에 전달하면 트랜잭션이 이미 종료되고 상태가 "Detached"로 변경됩니다. "분리됨"상태에서 변경하려는 항목은 모두 유효하지 않습니다. Hibernate는 당신이 그곳에서 무엇을 했는지 알 수 없습니다.
@Test
void testDecreaseStock() {
// Let's say the product id is 1
oldProduct.decrementStock(1);
Product newProduct = productService.find(1);
Logger.info("Product stock is: ", newProduct.getStock() )
}
제품 서비스에 대한 세부 정보는 다음과 같습니다.
@Transactional
public Product find(int id) {
return entityManager.find(Product.class, id);
}
@Transactional
public void decrementStock (int id) {
Product p = entityManager.find(Product.class, id);
p.setStock(p.getStock() - 1);
}
문제는 무엇 이었습니까? 🤔
처음 제품을 찾은 다음 다른 트랜잭션을 계속할 때 트랜잭션 범위를 벗어났기 때문에 "Detached"상태에서 제품을 변경하려고 했고 merge()를 사용하여 명령적으로 상태를 "Managed"로 변경했습니다. 우리는 "관리됨"상태에 있어야 한다는 것을 알고 있기 때문입니다. 다음은 제품을 변경하려는 범위입니다.
첫 번째 코드:
find(1) -> State: Managed -> product found -> Detached
find(1) -> State: Managed -> product found -> Detached
merge() -> State: Detached -> Managed -> stock updated -> Detached
find(1) -> State: Managed -> product found -> Detached
수정 코드:
find(1) -> State: Managed -> product found -> Detached
merge() -> State: Detached -> Managed -> stock updated -> Detached
find(1) -> State: Managed -> product found -> Detached
Reference
이 문제에 관하여(최대 절전 모드 - merge()를 피해야 하는 이유), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/yigi/hibernate-why-you-should-avoid-merge-41d9텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)