is_deleted로 조회시 삭제처리된 데이터는 감추고, 누가 언제 데이터를 삭제했는지 관리하기 위해 deleted_by와 deleted_at을 추가했습니다.
@SQLDelete
를 여러개 설정하지 못해서 deleted_by와 deleted_at는 따로 추가했습니다.
public void deleteOrder(UUID orderId) {
Long loginMember = SecurityUtil.getMemberIdFromSecurityContext();
// Order테이블 deleted_by, deleted_at 저장
Order order = orderRepository.findById(orderId)
.orElseThrow(()->new TwotwoApplicationException(ErrorCode.ORDER_NOT_FOUND));
order.setDeletedBy(loginMember);
order.setDeletedAt(LocalDateTime.now());
orderRepository.save(order);
log.info("order {}", order.toString());
// OrderProduct테이블 deleted_by, deleted_at 저장
order.getOrderProducts().forEach(orderProduct -> {
orderProduct.setDeletedBy(loginMember);
orderProduct.setDeletedAt(LocalDateTime.now());
orderProductRepository.save(orderProduct);
});
// @SQLDelete로 is_delted=true로 update쿼리 실행하기
orderRepository.deleteById(orderId);
}
Order와 OrderProduct에 deleted_by, deleted_at를 save()
하고 delete작업을 했는데
DB 테이블에 is_deleted만 true로 설정되고 deleted_by, deleted_at는 null값이 들어갔습니다.
@SQLDelete를 사용해서 delete()가 실행되지 않을거라 생각했지만, dirty check가 일어나지 않은 걸로 보아 delete()가 실행되어 entity가 영속성 컨텍스트에서 삭제되었다고 판단.
영속성 컨텍스트가 remove될 때 삭제되는지 직접 확인해 보려고 테스트해봤습니다.
@Test
@DisplayName("EntityTransaction 성공 테스트")
void delete() {
EntityTransaction et = em.getTransaction(); //EntityManager에서 EntityTransaction을 가져옵니다.
et.begin(); // 트랜잭션 시작
try{
Order order = em.find(Order.class, 4L);
order.setDeletedBy("admin");
order.getOrderProducts().get(0).setDeletedBy("admin");
em.persist(order);
em.remove(order);
et.commit(); // 오류가 발생하지 않고 정상적으로 수행되었다면 commit 호출
}catch(Exception ex){
ex.printStackTrace();
et.rollback(); // DB작업 중 오류 발생 시 rollback 호출
}finally{
em.close(); // 사용한 EntityManager 종료
}
emf.close(); // 사용한 EntityManagerFactory 종료
}
하지만 디버깅 결과 em.remove(order)
이후에도 persistenceContext에 값이 비워지지 않음.
persistenceContext가 그대로인데 왜 dirty check가 일어나지 않았는지 의문??
튜터님께 여쭤보고 찾아본 결과 enceContext 내부에 entityKeys가 남아 있는 것은 Hibernate가 내부적으로 트랜잭션 중 처리해야 할 엔티티 목록을 보관하고 랜잭션이 커밋되고 플러시될 때 최종적으로 제거된다고 합니다.
그리고 JPA 내부적으로 엔티티의 상태를 관리하여 DELETE로 마킹되면 dirty check가 발생하지 않는다고 합니다.
엔티티의 상태 관리는 EntityEntry에서 실행.
⬆️
em.continas(order)
로 영속성컨텍스트의 1차 캐시를 확인한 결과true
출력.
⬆️
em.unwrap(SessionImpl.class).getPersistenceContext().getEntry(order)
로 EntityEntry의 상태 확인 결과MANAGED
로 영속성 컨텍스트 내에서 관리되고 있는 것을 확인.
⬆️
em.remove()
에서 SessionImpl의 delete메서드 실행.
⬆️삭제가 발생하면 SessionImpl의 DeleteEventListener 리스너를 실행하여, 실제 onDelete() 메서드를 호출
DefaultDeleteEventListener
⬆️entityEntry의 값이 null이 아니고 상태가
MANAGED
이므로 넘어가서 deleteEntity호출
⬆️deleteEntity에서 EntityEntry의 상태를
DELETED
로 변경
⬆️
em.continas(order)
로 영속성컨텍스트의 1차 캐시를 확인한 결과false
로 1차 캐시에서 삭제.
⬆️
em.unwrap(SessionImpl.class).getPersistenceContext().getEntry(order)
로 EntityEntry의 상태 확인 결과DELETED
로 영속성 컨텍스트 내에서 더이상 관리하지 않음.
persistenceContext에는 여전히 값이 있지만 commit() 후 삭제.
entityManager의 remove()를 하더라도 persistenceContext에는 값이 있을 수 있다.
em.contains()
나 em.unwrap(SessionImpl.class).getPersistenceContext().getEntry()
로 엔티티가 영속성 컨텍스트에서 관리되고 있는 상태 인지 확인.