JPA 영속성컨텍스트 탐구

dh·2025년 2월 27일
0

JPA

목록 보기
4/4
post-thumbnail

is_deleted 외에 deleted_by, deleted_at컬럼 추가하다 발생한 문제

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에서 실행.

EntityEntry란?

  • Hibernate 내부에서 각 엔티티의 상태를 관리하는 객체
  • PersistenceContext 내부에 EntityEntry를 유지하면서 엔티티의 변경 사항을 추적

Order order = em.find(Order.class, 4L); 실행 결과

⬆️em.continas(order)로 영속성컨텍스트의 1차 캐시를 확인한 결과 true 출력.
⬆️em.unwrap(SessionImpl.class).getPersistenceContext().getEntry(order)로 EntityEntry의 상태 확인 결과 MANAGED로 영속성 컨텍스트 내에서 관리되고 있는 것을 확인.

em.remove(order) 처리 과정

⬆️em.remove()에서 SessionImpl의 delete메서드 실행.
⬆️삭제가 발생하면 SessionImpl의 DeleteEventListener 리스너를 실행하여, 실제 onDelete() 메서드를 호출

DefaultDeleteEventListener
⬆️entityEntry의 값이 null이 아니고 상태가 MANAGED 이므로 넘어가서 deleteEntity호출
⬆️deleteEntity에서 EntityEntry의 상태를 DELETED로 변경

em.remove(order) 실행 결과

⬆️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()로 엔티티가 영속성 컨텍스트에서 관리되고 있는 상태 인지 확인.

0개의 댓글

관련 채용 정보