JPA

테스트를 통해 JPA Entity 영속성에 대해 알아보자 (2)

자바지기 2022. 8. 29. 18:26
반응형

예시로 사용된 코드는 깃허브를 통해 확인할 수 있습니다. 

1편에 이어서 작성하는 글입니다.

이 글에서는 EntityManager의 merge 메서드에 대해 다룹니다.

 

 

학습을 진행하면서 이미 id 필드가 존재하는 Entity를 save 하면 어떻게 될까? 에 대한 의문이 생겼습니다.

이에 대해서 테스트를 진행하고, 헷갈리는 부분에 대해 정리해볼 수 있었습니다.

 

테스트는 아래의 Member Entity를 이용하여 진행하였습니다.

테스트를 위한 Entity

 

1. 이미 저장된 id를 가지는 Entity를 또 save 하면 어떻게 될까?

이에 대해 다음과 같은 테스트를 작성해보았습니다.

멤버1을 저장하고 멤버 1의 id를 이용해 멤버 2를 저장하였습니다.

이때 멤버 2를 저장하였지만, 저장된 결과로 반환된 저장된_멤버 2와는 다른 객체로 확인되었습니다.

 

또한 저장된_멤버 2는 멤버 1과 같은 객체로 확인되었습니다.

 

앞서 1편에서는 save에 사용되는 parameter Entity와 반환되는 Entity가 같다고 하였는데 

이와 모순되는 상황입입니다.

 

이 상황을 다음과 같이 확인할 수 있었습니다.

 

이미 id가 존재하는 Entity를 save 할 때는 merge 메서드가 실행됩니다.

 

merge메서드는 준영속 상태의 Entity를 영속 상태로 만들어주는 메서드입니다.

이때 준영속 상태의 Entity를 반환하지 않고 새로운 영속 상태의 Entity를 반환합니다.

 

그런데 위에서 저장된_멤버2 멤버1 같은 객체로 확인되었다고 했습니다.

즉, 새로운 영속 상태의 Entity와 이미 저장된 멤버 1이 같은 객체라는 것입니다.

 

이론적인 답은 다음과 같습니다.

 

1. 멤버 1을 save 하고, 멤버1을 영속성 콘텍스트에 삽입한다.

2. 멤버 2를 save 할 때, 멤버 2의 id 값이 영속성 콘텍스트 안에 존재하는지 확인한다.

3. 존재한다면 em.merge의 결과로 영속성 콘텍스트에 존재하는 Entity를 반환한다.

 

영속성 컨텍스트에 대한 코드를 확인해보겠습니다.

 

위의 테스트 코드를 디버깅하다 보면 DefaultMergeEventListener의 onMerge 메서드를 실행합니다.

 

onMerge 메서드의 146번 줄부터 영속성 콘텍스트에 대한 내용을 확인해볼 수 있었습니다.

이후 줄부터는 다음과 같은 코드를 실행합니다.

 

1. parameter로 넘겨진 Entity의 id 값을 이용하여 EntityKey를 생성합니다. (152번 줄)

2. EntityKey를 이용하여 영속성 콘텍스트에서 관리하는 managedEntity를 꺼내옵니다.

3. managedEntity의 필드 값을 parameter로 넘겨진 Entity가 가지고 있는 값으로 변경합니다.

4. managedEntity를 반환한다.

 

결과적으로 정리하면 다음과 같습니다.

 

1. 멤버 1을 save 하고, 멤버 1을 영속성 콘텍스트에 삽입한다.

2. 멤버 2를 save 할 때, 멤버 2의 id 값이 영속성 콘텍스트안에 존재하는 지 확인한다.

3. 존재한다면 영속성 컨텍스트 안에 있는 Entity 내부의 필드 값을 update 한다.

4. 멤버 1은 계속해서 영속성 콘텍스트의 관리대상이 된다.

5. 멤버 2는 영속성 콘텍스트의 관리대상이 아니다.

 

 

2. 그렇다면 이전에 저장되지 않은 id를 가지는 Entity를 또 save 하면 어떻게 될까?

이 경우에는 2가지 경우에 대해 테스트해보았습니다.

 

1. id 생성 전략이 있는 경우

2. id 생성 전략이 없는 경우

 

Id 자동 생성 전략이 있는 경우에 대해 확인해보겠습니다.

위에서 사용된 Entity를 한번 더 이용해서 테스트를 진행해보겠습니다.

 

위와 같이 DB에 존재하지 않는 id값을 가지고 있는 Entity를 save 해보았습니다.

이때도 마찬가지로 저장된_멤버멤버는 다른 객체였습니다.

그러나 이는 위에서 보았던 것과는 조금 다른 상황입니다.

저장된_멤버가 가지고 있는 id 값이 99999999L 일 것으로 예상했지만 아니었습니다.

 

이는 Id 생성 전략을 다음과 같이 적어주었기 때문입니다.

@GeneratedValue(strategy = GenerationType.IDENTITY)

따라서 99999999L 은 생성 전략으로 생성되는 값이 아니었으므로 DB에서 자동 생성된 값으로 id에 저장된 것입니다.

 

그렇다면 Id 생성 전략이 없는 경우는 어떻게 될까?

테스트로 다음과 같은 Entity를 사용했습니다.

아래의 테스트를 확인해보면 쉽게 상황을 이해할 수 있습니다.

Id 생성 전략이 없는 경우는 DB에 없는 id값을 저장하여도 전달해준 id값으로 save 합니다.

또한 merge() 메서드를 실행하므로 새로운 객체를 반환하게 됩니다.

반응형