Whiteship's Note


[하이버네이트] detached 객체의 동일성

Hibernate/Chapter 9 : 2009.06.25 13:54


참조: JPWH 9.2.2 ~ 9.2.3

두 가지 동일성이 있다. 자바 객체 동일성과 DB 동일성이 있다. 자바 동일성은 == 으로 비교를 하고, DB 동일성은 주키 값을 비교한다. 자바 동일성과 DB 동일성이 모두 같을 조건을 Scope of object identity 라고 한다.

그 중에 세 가지 조건은 다음과 같다.
- No identity scope
- Persistence context-scoped identity
- Process-scoped identity

이 중에 하이버네이트는 Persistence context-scoped identity를 구현했다.

Session session1 = sessionFactory.openSession();
Transaction tx1 = session1.beginTransaction();

// "1234" 식별자로 Item 가져오기
Object a = session1.get(Item.class, new Long(1234) );
Object b = session1.get(Item.class, new Long(1234) );

( a==b ) // True, a와 b는 동일하다.
tx1.commit();
session1.close();

// a와 b 레퍼런스는 detached 상태 객체가 된다.

Session session2 = sessionFactory.openSession();
Transaction tx2 = session2.beginTransaction();

Object c = session2.get(Item.class, new Long(1234) );
( a==c ) // False, detached 객체 a와 persistent 객체 c는 동일하지 않다.(session context가 다르기 때문에..)

tx2.commit();
session2.close();

따라서 이렇게 된다. 하지만 여전히 DB id는 가지고 있기 때문에, a, b, c를 id 값으로 비교하면 모두 같은 객체로 인식할 수 있다. equlas()로 비교할 땐 equals()를 어떻게 구현하느냐에 따라 달리질 것이다. equals()를 별도로 구현하지 않으면 Object의 equals()를 사용할테니 == 비교와 다를 바가 없다.

equals() 비교가 중요해지는 시기는 Set 컬렉션을 사용할 때다. 아시다시피 Set은 컬렉션에 요소를 추가하기 전에 기본에 추가되어 있는 것들과 equals() 비교를 해본 다음에 false인 것만 추가한다.

그렇다면, 위의 코드가 모두 깥는 뒤 (detached) 객체 3개(a, b, c)를 Set에 추가하면 어떻게 될까? 과연 Set에는 몇 개의 객체가 들어갈까?

2개다. 일단, equals()를 별도로 구현하지 않아서, == 비교를 할 텐데 이미 위에서 == 비교를 해봤더니 a와 b는 같은 객체를 참조하고 있고, c는 또 다른 객체기 때문이다.

원하던 결과는 몇 개 일까? 한 개일 것이다. 셋 다 같은 DB 레코드를 가리키기 때문에, Set에는 한 개만 들어가는게 맞을 것이다. 그렇게 하려면 어떻게 해야 할까?

DB id를 사용해서 동일성을 비교하는 equals()를 재정의하면 될 것이다. equals()를 재정의하면 hashCode()도 반드시 재정의해서 같은 객체는 같은 해쉬코드를 반환하도록 해야겠다.

결론적으로, detached 객체를 다룰 때는 동일성에 주의하고, 동일성 문제가 발생할 시 equals()와 hashCode()를 적절하게 재정의 할 것.




top

  1. Favicon of http://blog.lckymn.com BlogIcon Kevin 2009.06.26 01:48 PERM. MOD/DEL REPLY

    제가 알기로 HashSet같은 Set은
    이미 저장된 객체들의 equals() 를 먼저 비교하는게 아니라
    저장된 객체들의 hashCode()를 먼저 다 비교해 본후에 동일 hash code가 없으면
    동일 객체가 없는것으로 판단해서 저장을 하고,
    동일 hash code가 발견되면 그때 equals() 비교를 해서
    true면 동일 객체로 판단해서 저장을 하지 않고,
    false면 다른 객체로 판단해서 저장을 하는것으로 알고 있습니다.

    위의 경우 말고도 문제가 되는 경우가 있겠죠.
    가령 DB에서 id로 쓰는 자동생성되는 surrogate key 같은걸
    equals()와 hashCode() 에 사용해 버린 객체를 DB저장전에
    HashSet에 넣었다가 DB에 저장을 하면
    저장 전에는 id값이 null (Integer 나 Long 타입) 이나 0 이었다가
    저장 되면서 id값을 받게 되면서, hashCode() 와 equals() 결과값이 변해서
    이미 저장해 놓은 HashSet에 존재하지 않는 새로운 객체로 인식을 해버리는
    문제가 생길수 있겠죠.
    (둘중 하나만 달라도 충분히 다른 객체로 인식하겠지만요.)

    아... 오늘도 묻어가기...ㅡ_ㅡ;;;

    Favicon of http://whiteship.me BlogIcon 기선 2009.06.26 08:30 PERM MOD/DEL

    그렇군요; hashCode()가 먼저였군요.

    ㅎㅎ오늘도 감사합니다.

  2. Favicon of http://kwon37xi.egloos.com BlogIcon 권남 2009.06.29 09:30 PERM. MOD/DEL REPLY

    Kevin 님이 하신말씀에 부연설명 합니다.

    ID 값만으로 hashCode와 equals를 생성하면 id == null인 상태의 객체 여러개를 Set에 저장할 때 문제를 일으킵니다. 따라서 절대로 그런일 이 없게 하거나, 아니면 createdAt 같이 객체의 ID 값 처럼 절대 불변하는 어떤 값도 함께 equals와 hashCode에 추가해주어야 합니다.

    최선책 : 객체의 ID와 그외의 불변하는 어떤 을 더 추가하며 hashCode/equals를 만든다.
    차선책 : ID만으로 hashCode/equals를 구현하고 절대로 DB에 저장하기 전에 먼저 Set에 저장하는 일이 없도록 개발자들에게 경고한다.

    Favicon of http://whiteship.me BlogIcon 기선 2009.06.29 18:32 PERM MOD/DEL

    넹. transient 상태 객체는 아직 id가 없을테니깐 보조키(?)도 같이 활용해서 hashCode와 equals를 구현하는게 좋겠네요.

Write a comment.


객체 상태

Hibernate/Chapter 9 : 2008.02.24 13:56


특징

  • ORM 솔루션마다 서로 다른 용어와 서로 다른 상태를 정의하고 영속성 라이프사이클의 상태 전이를 다룬다.
  • 하이버네이트는 네 가지 상태를 가지고 있다.

  • 이탤릭체의 메소드 이름은 JPA의 EntityManager와 Hibernate의 Session에서 공통으로 사용할 수 있는 API

Transient Object

  • new 연산자를 사용하여 생성한 객체는 곧바로 persistent 상태가 되지 않는다.
  • 데이터베이스의 레코드와 아무 연관이 없기 때문에, 메모리에서 없어지면 그 상태 정보도 없어진다.
  • JPA에는 이 상태를 나타내는 용어가 없다.
  • 이 상태의 객체는 nontransactional로, Persistence Context에 변경 사항이 기록되지 않는다. 따라서 롤백을 지원하지 않는다.
  • 이 상태의 객체가 참조하는 객체들도 기본적으로는 Transient 상태다.

Persistent Object

  • 데이터베이스 식별자를 가지고 있는 엔티티다.
  • Persistent 상태의 객체에 의해 참조되었기 때문에 이 상태가 되는 객체도 있다.
  • DB에서 쿼리, 식별자 룩업, 다른 Pesistent 객체로 부터 시작한 객체 그래프 네비게이션 등을 통해서 이 상태가 되는 객체도 있다.
  • 이 상태의 객체는 항상 Persistent Context와 연관을 맺고 있다.
  • 하이버네이트는 그들을 캐쉬하고 애플리케이션 내에서의 변경 사항을 감지할 수 있다.

Removed Object

  • 엔티티 개체를 여러 방법으로 제거할 수 있다.
  • Persistent Manager의 메소드를 사용하여 제거할 수 있다.
  • Orpahn Deletion(해당 하는 객체를 참조하는 녀석이 없을 때 지우기)을 할 수 있다.
  • Unit of work가 끝나면 곧 사라질 객체지만 여전히 Persistent Context에는 남아있을 수 있다.
  • 따라서, 애플리케이션 내에서 해당 객체에 대한 참조를 모두 제거해야 한다.

Detached Object

  • Unit of work를 마친 뒤에 여전히 애플리케이션에서 참조되고 있는 객체.
  • Persistent Context가 활성화 되어 있을 동안 만 Persistent 상태였다.
  • 이 상태의 객체들은 더이상 DB와 동기화 하지 않는다.
  • Persistent Context에 연관되어 있지 않다.
  • 이 상태의 객체를 가지고 여러 변경 작업을 할 수 있고, 후에 다시 Persistent 상태로 돌리고 싶을 수 있다.
  • 하이버는 그런 상황을 위해 reattachment와 merging을 제공한다.
  • JP는 오직 merging만 표준화했다.
  • Conversation: long units of work

'Hibernate > Chapter 9' 카테고리의 다른 글

하이버네이트 API : Persistence context 관리하기  (0) 2008.03.11
하이버네이트 API : Detached 객체 다루기  (0) 2008.03.11
하이버네이트 API: 저장하고 읽어들이기  (0) 2008.03.11
Reattaching과 Merging  (2) 2008.03.11
Persistence context 확장하기  (0) 2008.02.24
The identity of detached objects  (0) 2008.02.24
The Scope of Object Identity  (0) 2008.02.24
Conversation 소개  (0) 2008.02.24
Persistence Context  (0) 2008.02.24
객체 상태  (0) 2008.02.24
Persistence Lifecycle  (0) 2008.02.24
top

Write a comment.