안녕하세요. 회사와 함께 성장하고 싶은 KOSE입니다.

이번 포스팅은 Jpa save()에 대한 정리를 진행하고자 합니다.!

 

오늘 새벽에 개발 관련 카카오톡 채팅방에서 다음의 질문이 올라왔습니다.

BaseEntity()에 createAt, modifiedAt을 설정하고 타임스탬프를 6자리 설정했으나,

save()를 수행한 값과 조회한 값의 타임스탬프가 동일하지 않다는 것이었습니다.!

 

이 문제의 원인은 save()라는 함수(메서드)에 있었습니다.

함께 save()를 정리하여 위의 문제를 해결해보겠습니다.

 

 

1. save()

 

 

Jpa의 가장 많이 쓰이는 함수인 save()는 엔티티를 영속화하여 데이터베이스에 저장하는 역할을 수행합니다.

 

 

Jpa의 save()를 살펴보면 다음의 메커니즘으로 처리가 됩니다.

 

 

코드를 분석하면, S 타입의 entity를 인자로 받아,

식별자가 존재하는 엔티티를 판단하여 처음이라면, entityManager로 영속화한 후, entity를 리턴합니다.

이미 식별자가 존재한다면 entityManager.merge(entity)를 수행하여 병합합니다.

 

이 코드는 인자로 받는 엔티티가 다시 리턴되는 구조로 되어있습니다.
이는 곧 입력받는 인자와 리턴하는 인자는 동일 인스턴스를 의미하는 것입니다.
만약 엔티티가 새로운 값이라면, persist를 통해 setter로 id 식별자를 생성하게 됩니다.

 

여기서 궁금한 사항이 생길 수 있습니다!

대부분은 이제 @Setter를 쓰지 않고 @Noargsconstructor를 쓰며 엔티티를 생성하실 겁니다.

setter는 위험하니, 지양하고 계실 것 같습니다!

 

만약 @Setter를 쓰지 않음으로써 id 필드(혹은 프로퍼티)를 변경 불가능하도록 한다면,
어떻게 persist가 해당 필드를 수정할 수 있는 것일까요?

 

Reflection

 

 

이는 자바의 리플렉션과 관련이 있습니다.

JPA 구현체는 Java의 Reflection API를 사용하여 private 필드에도 직접 접근하고 값을 설정할 수 있습니다.

리플렉션으로 id 필드에 접근한 후, 접근 가능토록 변경한 후, set을 통해 식별자를 바꿔줄 수 있습니다.

 

이를 통해, save()는 함수의 인자와 리턴되는 값은 동일한 객체이되 새로운 엔티티라면 리플렉션으로 식별자를 setter로 등록한다고 할 수 있습니다.

 

 

다음처럼 테스트 코드로 살펴보겠습니다.

새로운 엔티티를 생성하고 save()를 저장한 후 리턴한 값을 savedRestaurantEntity로 설정했습니다.

savedRestaurantEntity는 객체 생성에 설정했던 address의 객체를 그대로 참조하고 있으며, 
shouldBeSameInstanceAs로 생성 시 엔티티와 저장후 엔티티가 동일한 객체 인스턴스임을 검증하고 있습니다.

 

 

2. 타임스탬프가 맞지 않았던 문제

 

문제의 발단은 다음과 같습니다.

 

 

해당 코드처럼 BaseEntity를 생성했음에도 불구하고, 생성시 타임스탬프와 저장 후 조회한 값의 타임스탬프가 같지 않은 것입니다.

 

 

이는 위에서 설명했던 save()의 개념으로 해결할 수 있습니다.

 

 

객체를 생성할 때는 LocalDateTime()의 기본값으로 나노초가 설정이 됩니다.

save()를 하더라도 식별자가 생길 뿐 객체 자체에는 변화가 없습니다.

 

이는 이전에 설정한 TIMESTAMP가 5 더라도, 조회한 값을 가져온 것이 아니므로 객체 자체의 온전한 값이 저장되는 것입니다.

하지만, 실제 조회를 하게 되면, DB에 넣은 값을 조회할 수 있으므로 다음처럼 결과를 얻을 수 있습니다.

 

 

따라서, createdAt, modifiedAt을 쓸 때,

save()를 한 후 식별자로 한 번 더 조회한 값으로 테스트를 수행해야 원하는 값을 얻을 수 있습니다.

(영한님의 JPA 강의에서 save()한 값을 그대로 활용할 때 원하지 않는 문제가 발생할 수 있다고 하셨던 말이 떠오르네요!)

 

이상으로 Jap save()에 대한 정리를 마치도록 하겠습니다. 

읽어주셔서 감사합니다!

+ Recent posts