2023. 11. 8. 11:34ㆍProject 해축갤/테스트 코드
오류 이해하기
Detached(준영속) 상태란?
Hibernate 및 JPA에서 엔티티의 생명주기 중 하나인 'Detached' 상태는
영속석 컨텍스트가 더 이상 해당 객체를 추적하지 않을 때 발생합니다.
세션이 닫히거나(close), 클리어될 때(clear), 또는
수동으로 영속성 컨텍스트에서 제거(detach) 될 때 이 상태에 도달할 수 있습니다.
Entity의 상태 코드 EntityState enum을 보면
public enum EntityState {
PERSISTENT, TRANSIENT, DETACHED, DELETED;
static final CoreMessageLogger LOG = CoreLogging.messageLogger( EntityState.class );
/**
* Determine whether the entity is persistent, detached, or transient
*/
public static EntityState getEntityState(
Object entity,
String entityName,
EntityEntry entry, //pass this as an argument only to avoid double looking
SessionImplementor source,
Boolean assumedUnsaved) {
if ( entry != null ) { // the object is persistent <-- entry 에는 id 가 포함!!
//...
}
if ( ForeignKeys.isTransient( entityName, entity, assumedUnsaved, source ) ) {
//...
}
if ( LOG.isTraceEnabled() ) {
//...
}
return DETACHED;
}
}
중간에 if ( entry != null ) 파트가 있는 데, entry의 내부 코드에는
id 가 존재합니다.
즉, 우리는 Hibernate 가 id가(Entity 기준 @Id 가 달린 필드의 값)
있다 없다를 기준으로 Detached 인지 아닌지를 가른다는 걸 알 수 있습니다.
Persist vs Merge
persist() 새로운 객체를 영속성 컨텍스트에 추가할 때 사용됩니다.
객체가 아직 영속성 컨텍스트에 저장되지 않았을 때 적합합니다.
merge()는 Detached 상태의 객체를 다시 영속 상태로 전환할 때 사용됩니다.
객체의 ID 필드를 기반으로 컨텍스트에 다시 연결합니다. (이 부분을 기억!!)
문제 상황
왜 이런 글을 쓰게 됐냐? 제가 겪었기 때문에,,,
저는 해축갤 게시판의 Member 엔티티의 단위 테스트를 작성 중이었습니다.
해당 테스트는 회원 정보가 정상적으로 저장되는지 확인하는 과정입니다.
그러나 테스트 실행 시 'Detached Entity Passed to Persist'라는 오류가 발생했습니다.
에러 원인 분석
코드는 아래와 같습니다.
Member member = Member.builder()
.id(1L)
.name("tester")
.email("test@example.com")
.password("password")
.build();
em.persist(member);
이 코드에서 Member 객체는 id 값을 이미 가지고 있기 때문에
Hibernate는 이미 이 객체를 식별 가능한, 즉 detached 상태로 간주할 수 있는 객체로 인식합니다.
따라서 persist 메서드에서 위와 같은 오류가 발생하는 겁니다.
해결 방안
즉, id 가 있어서 생기는 문제이기에 2가지 접근 방법을 생각할 수 있습니다.
- persist를 사용하되, 객체 생성 시 'id'를 설정하지 않습니다.
- merge 메서드를 사용하여 Detached 상태의 객체를 다시 영속성 컨텍스트에 연결합니다.
저는 persist를 사용하는 첫 번째 방법을 선택하였습니다.
왜냐하면 아직 데이터베이스에 엔티티가 존재하지 않고,
새롭게 데이터베이스에 넣으려고 하는 상황이기 때문입니다.
merge 의 두 사용처는 영속성 컨텍스트로 다시 관리하겠다 입니다.
즉, 기존의 영속 엔티티와 메모리상(즉 코드레벨에 존재하는)의 엔티티와 결합할 때 사용되겠죠.
물론 영속성 컨텍스트에 merge 하고자 하는 엔티티가 존재하지 않으면 저장이 되겠다만은.
결론
Persist와 Merge 메서드 사용, 그리고
Hibernate 가 여러 기준 중 id 값을 토대로 Entity의 상태를 결정할 수 있다는 것을 알았습니다.
여러분은 이런 에러 만나시면 후딱 넘기시길!
참고
https://www.baeldung.com/hibernate-detached-entity-passed-to-persist
'Project 해축갤 > 테스트 코드' 카테고리의 다른 글
Java @Scheduled: 매시 정각에 실행되는 Cron 표현식 테스트하기 (1) | 2023.12.20 |
---|---|
테스트 띠띠ㅣ디ㅣ디ㅣ딛ㄹ딸깍? postman 으로 '띡띡딸깍' 하기 (1) | 2023.11.30 |
org.mockito.exceptions.misusing.UnnecessaryStubbingException 해결 (0) | 2023.11.07 |
테스트를 더 빨리 끝내보기 (feat. 자료구조, singletonList) (0) | 2023.11.02 |
테스트 코드 작성 시 유의할 점 (Mockito doNothing) (1) | 2023.11.02 |