새소식

JPA

엔티티가 비영속이었다가 영속됐다가 준영속됐다가 다시 영속됐다가 ...더보기

  • -

 

안녕하세요

 

문제는 그렇게 시작된

 

건 아니고 그냥 엔티티의 상태에 한번 정리해보려고 합니다

 

렛츠고

 

일단 영속성 컨텍스트는 엔티티를 영구 저장하는 환경으로 1차 캐싱, 쓰기 지연, 변경 감지를 통해 영속 로직을 효율적으로 할 수 있게 해줍니다

 

그리고 엔티티 매니저는 이 영속성 컨텍스트를 관리하는 작업을 도와주는 역할로, 영속성 컨텍스트와 상호작용함으로서 영속 로직을 수행하는 역할을 가지고 있습니다

 

엔티티 매니저에 의해 관리되는 엔티티는 영속성 컨테이너에 대해 세 가지 상태를 갖습니다

 

비영속

엔티티 객체가 새로 생성되었지만 아직 영속성 컨텍스트와 연관되지 않은 상태입니다

 

public void create(String id, String name) {
    Study study = new Study(id, name);
}

 

이렇게 그냥 객체가 하나 생성됐다 보시면 됩니다

 

영속

엔티티 객체가 영속성 컨텍스트에 의해 관리되고 있는 상태로, 엔티티의 변경 사항이 자동으로 DB에 반영됩니다

이걸 좀 변경 감지라 합니다

 

@Transactional
public void create(String id, String name) {
    Study study = new Study(id, name);
    System.out.println("study persist status : " + em.contains(study));
    
    em.persist(study);
    System.out.println("study persist status : " + em.contains(study));
}

 

실제로 위 코드 실행 시

study persist status : false
study persist status : true

 

비영속 상태에는 영속성 컨텍스트(em)에 포함되어 있지 않고 영속(persist) 해줄 시 영속성 컨텍스트에 포함됩니다

 

준영속

엔티티 객체가 영속성 컨텍스트에 의해 관리되었다가 현재는 관리되지 않는 상태로, 더이상 엔티티의 변경 사항이 DB에 반영되지 않습니다

즉 변경감지가 일어나지 않습니다

 

영속성 컨텍스트 종료, 트랜잭션 종료 등으로 준영속 상태로 전환됩니다

// StudyService.java
public void study(String id, String newName) {
    Study study = study2Service.getStudy(id);
    study2Service.changeStudy(study, newName);
}

// Study2Service.java
@Transactional(readOnly = true)
public Study getStudy(String id) {
    return studyRepository.findById(id).get();
}

@Transactional
public void changeStudy(Study study, String newName) {
    System.out.println("study is persist status : " + em.contains(study));
    study.setName(newName);
}

 

실제로 위와 같은 코드에서 study 객체를 조회하여 영속 상태였음에도 불구하고 getStudy의 트랜잭션이 끝나면서 study 객체가 준영속 상태로 변하면서 changeStudy에서 변경한 정보가 반영되지 않게 됩니다

 

Hibernate: 
    select
        s1_0.id,
        s1_0.name 
    from
        study s1_0 
    where
        s1_0.id=?
study is persist status : false

 

 

실제로 영속성 컨테이너에도 포함되어 있지 않고 update 쿼리도 날라가지 않습니다

 

그럼 이를 해결할 수 있는 방법은 뭐가 있을까요?

 

첫 번째로 study에 트랜잭션을 걸어주는 겁니다

하나의 큰 트랜잭션이 걸리며 해당 트랜잭션 안에서 study는 영속성 컨텍스트에서 관리됩니다

 

두 번째로 study를 다시 영속화 시켜주는 겁니다

 

merge 메서드를 통해 준영속 엔티티를 다시 영속화 시킬 수 있습니다

@Transactional
public void changeStudy(Study study, String newName) {
    em.merge(study);
    System.out.println("study is persist status : " + em.contains(study));
    study.setName(newName);
}

 

이렇게 영속화를 시키면

 

Hibernate: 
    select
        s1_0.id,
        s1_0.name 
    from
        study s1_0 
    where
        s1_0.id=?
Hibernate: 
    select
        s1_0.id,
        s1_0.name 
    from
        study s1_0 
    where
        s1_0.id=?
study is persist status : false

 

다시 DB에서 조회를 해서 영속화를...

 

엥 근데 왜 persis status가 false죠? 영속화가 안됐다는데요?

 

그 이유는 merge 시 객체가 영속성 컨텍스트에 등록되며 새로운 객체가 반환되기 때문에 그렇습니다

 

진짜 그럴까요?

 

@Transactional
public void changeStudy(Study study, String newName) {
    Study mergedStudy = em.merge(study);
    System.out.println("study is persist status : " + em.contains(study));
    System.out.println("mergedStudy is persist status : " + em.contains(mergedStudy));
    System.out.println("study = " + study);
    System.out.println("mergedStudy = " + mergedStudy);
    mergedStudy.setName(newName);
}

 

이렇게 merge가 반환하는 객체에 대해 상태와 각 객체 study, mergedStudy를 출력하고 mergedStudy의 name을 바꿔보겠습니다

 

Hibernate: 
    select
        s1_0.id,
        s1_0.name 
    from
        study s1_0 
    where
        s1_0.id=?
Hibernate: 
    select
        s1_0.id,
        s1_0.name 
    from
        study s1_0 
    where
        s1_0.id=?
study is persist status : false
mergedStudy is persist status : true
study = knu_chatbot.entity.Study@12680685
mergedStudy = knu_chatbot.entity.Study@4eb168a1
Hibernate: 
    /* update
        for knu_chatbot.entity.Study */update study 
    set
        name=? 
    where
        id=?

 

그럼 앞의 경우와는 다르게

 

1. mergedStudy의 상태가 영속으로 변함

2. 변경 감지에 의해 mergedStudy의 name이 update 되었음

 

을 알 수 있고 실제로 study와 mergedStudy를 출력해보면 서로 다른 객체(Study@12680685, Study@4eb168a1)를 나타내고 있음을 알 수 있습니다

 

오늘의 결론

 

영속, 준영속을 잘 관리하고 트랜잭션 관리도 잘하고

뭐 그 뭐시기

 

 

예 그 뭐시기 화이팅입니다

Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.