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


Hibernate VS JPA

Hibernate/Chapter 9 : 2008.03.11 15:09


Hibernate JPA
네 가지의 상태(transient, persistent, removed, detached)가 있다. 이것들을 표준화했다.
detached 객체를 merging하거나 reattach할 수 있다. merging만 지원한다.
flush 시점에서 save()와 update()는 모든 연관된 객체들에 영향을 준다. persist()는 호출 시점에 연관되어 있는 객체들에만 영향을 준다. flush 시점에서 persist()는 모든 연관된 객체들에 영향을 준다. save()와 update()는 해당 메소드를 호출하는 시점에 연관되어 있는 객체들에만 영향을 준다.
get()은 DB에 다녀오고, load()는 프록시를 반환한다. find()는 DB를 다녀오고, getReference()는 프록시를 반환한다.
EJB에서 Session을 사용할 때 Depedency Injection은 JBoss Application Server만 지원한다. EntityManager의 DI는 모든 EBJ 3.0 호환 서버에서 지원한다.
top


하이버네이트 API : Persistence context 관리하기

Hibernate/Chapter 9 : 2008.03.11 15:07


Persistence Context 캐시 제어하기

  • Persistent 객체들의 스냅샷들을 캐시에 복사해둔다.
  • 이 캐시들을 사용하여 dirty checking을 하여 persistent 객체들의 변경 사항들을 찾아낸다.
  • 수천개의 객체들을 로딩하면, OutOfMemoryException을 내고 죽어버릴 수가 있다.
  • 캐시를 줄이거나 메모리 공간을 제약하려면 다음과 같이 해야 한다.
    1. 필요한 객체만 Persistent 상태로 유지하라. 전체 객체 그래프를 모두 끌어오다가는...
    2. session.evict(object)를 사용해서 persistent 상태의 객체를 명시적으로 detached 시킬 수 있다.
    3. session.clear()를 사용해서 Persistent Context에 있는 모든 객체를 detached로 만들 수 있다. 이 녀석들은 dirty checking하지 않는다.
    4. session.setReadOnly(object, true)를 사용해서 특정 객체에 대해서는 dirty checking을 하지 않도록 설정할 수 있다. false를 주면 다시 한다. 객체의 상태는 바꾸지 않는다.

Persistent Context flushing 하기

  • 하이버는 Persistent 객체들에 대한 변경 사항을 바로바로 DB에 반영하지 않는다.
    1. DB 요청을 최소화 할 수 있다.
    2. DB 롹킹 기간을 최소화 한다.
    3. JDBC batch API의 장점을 취할 수 있다.
  • flush(): DB와 Persistent Context의 동기화
    1. Transaction(하이버의 API)이 commit() 될 때.
    2. 쿼리를 실행하기 전에..
    3. session.flush()를 호출 할 때.
  • session.setFlushMode()를 사용할 수 있다. 기본값은 FlushMode.AUTO.
  • FlushMode.COMMIT으로 설정하면
    1. 쿼리를 실행하기 전에.. 동기화를 하지 않는다.
    2. 오직 Transaction.commit()과 session.flush()를 할때 만 동기화한다.
    3. 쿼리 -> 수정 -> 쿼리 -> 수정. 이런 경우에 FlushMode를 Commit으로 설정해주면 효율적이다.
  • FlushMode.MONUAL로 설정하면
    1. 오직 session.flush()를 호출 할 때만 동기화한다.
  • FlushMode 제어는 나중에 Persistent Context를 Conversation으로 확장할 때 사용할 것이다.
  • 중요한 것은 flush 처리의 성능은 persistent context의 크기에 따라 달라진다는 것이다.
top


하이버네이트 API : Detached 객체 다루기

Hibernate/Chapter 9 : 2008.03.11 15:06


수정된 detached 객체 reattaching 하기

  • update()를 사용하여 새로운 session에 reattach 할 수 있다.
item.setDescription(...); // Loaded in previous Session

Session sessionTwo = sessionFactory.openSession();
Transaction tx = sessionTwo.beginTransaction();

sessionTwo.update(item);

item.setEndDate(...);

tx.commit();
sessionTwo.close();
  • update()에 넘겨주는 객체가 그 전에 수정이 되었는지 안 되었는지는 중요하지 않다.
  • 새로운 Persistent Context에 묶이게 된다는 것이 중요하다.
  • 새로운 Persistent Context에서는 무조건 dirty로 인식하고 나중에 update문을 날리게 된다.
  • DB에서 실제 값과 비교하여 바뀐 부분이 있을 때에만 update문을 날리고 싶으면 select-before-update="true"를 설정한다.
  • 수정되지 않았다는 것이 활실할 때는 update를 사용하지 않는 reattach 방법을 사용한다.

수정되지 않은 detacked 객체를 reattaching 하기

  • lock()을 사용하면 그 시점 부터 DB에 반영을 합니다. 그 이전의 변경 사항은 반영하지 않습니다.
Session sessionTwo = sessionFactory.openSession();
Transaction tx = sessionTwo.beginTransaction();

sessionTwo.lock(item, LockMode.NONE);

item.setDescription(...);
item.setEndDate(...);

tx.commit();
sessionTwo.close();
  • 위에서 사용한 LockMode.NONE은 새로운 객체를 Session에 붙일 때, 버전 확인이나 DB 레벨의 롹을 가져오지 않겠다는 설정입니다.

detacked 객체를 transient 객체로 만들기

  • update()나 lock()를 호출하열 필요 없이 그냥 delete()를 사용합니다.
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();

session.delete(item);

tx.commit();
session.close();
  • 그 안에서 해당 객체를 다시 Session에 붙였다가 tx.commit()이 호출되는 시점에 DB에서 삭제합니다.

detached 객체 mergeing 하기

  • reattachment의 보완제이자 대체제다.
Session session1 = sessionFactory.openSession();
Transaction transaction = session1.beginTransaction();

Member member = new Member();
member.setName("toby");
session1.save(member);

transaction.commit();
session1.close();

member.setName("whiteship");

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

Member member2 = (Member) session2.get(Member.class, member.getId());
Member member3 = (Member) session2.merge(member);

member2.setName("toby");

assertEquals("toby", member3.getName());

transaction2.commit();
session2.close();
  • NonUniqueObjectException: reattack 할 때, 이미 동일한 레코드를 표현하는 객체가 해당 Persistent Context에 존재할 때 발생하는 예외.
  • Persistent Context에 만약, 같은 DB 레코드를 나타내는 객체가 있다면, detached 객체의 값들을 해당 객체로 복사한다.
  • Persistent Context에 존재하지 않는 객체일 경우에는, DB에서 같은 id를 가진 녀석을 읽어온 다음에 merge() 한다.
  • detached객체나, 기존의 Persisitent Context에 있던 객체의 사용은 지양하고, merge()가 반환하는 객체를 사용하도록 한다.
top


하이버네이트 API: 저장하고 읽어들이기

Hibernate/Chapter 9 : 2008.03.11 15:05


Unit of work 시작하기

Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
  • SessionFactory는 매번 생성하지 말 것. 비싸다. Spring에서는 singleton으로 사용하니까 그럴 걱정은 없겠군. Thread Safe 하겠지?
  • Session은 비싸지 않다. 필요하기 전까지는 JDBC Connection조차 가져오지 않는다.
  • 위에서는 하이버의 Transaction API를 사용해지만, 꼭 저것만 사용해야 하는 건 아니다. 다음 챕터에서 자세히 다룬다.

객체를 persistent 상태로 만들기

Item item = new Item();
item.setName("Playstation3 incl. all accessories");
item.setEndDate( ... );

Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();

Serializable itemId = session.save(item);

tx.commit();
session.close();
  • save()를 호출하면 persistent context에 묶이게 된다.
  • 언젠가는 이 persistent context와 DB가 동기화 되어야 한다.
  • Transaction의 commit()을 호출할 때 flush가 일어나고, 이것을 flush()로 명시적으로 호출할 수도 있다.
  • session.save()이후의 모든 변경사항들은 추가적인 update문으로 DB에 반영된다.
  • session.beginTransaction()와 tx.commit()사이의 변경 사항들을 DB에 반영할 때, 예외가 발생하면 모든 SQL을 롤백한다.
  • 그러나 Persistent 객체의 메모리 상태는 롤백되지 않는다.

Persistent 객체 가져오기

  • 두 가지 방법이 있다. get(), load()
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();

Item item = (Item) session.load(Item.class, new Long(1234));
// Item item = (Item) session.get(Item.class, new Long(1234));

tx.commit();
session.close();
  • session에서 가져오는 순간 persistent 상태이며, session이 끝나는 순간 detached 상태가 된다.
  • id에 대응하는 레코드가 없을 때, get()은 null을 반환한다. load()는 ObjectNotFoundException을 던진다.
  • load() 메소드는 실제로 DB에 다녀오지 않고 Proxy를 반환한다. 단, 현재 Persistent Context에 이미 대응하는 id를 가진 객체가 존재한다면, 초기화까지 되어 있는 객체를 돌려준다.
  • get() 메소드는 절대로 Proxy를 반환하지 않는다. 항상 DB에 다녀온다.
  • DB에 있는 객체 하나를 꺼내서 다른 객체에 세팅해줘야 하는 경우. Comment.setForAuction(item). 이 경우 굳이 item은 load()로 가져와도 된다. 굳이 DB에서 전부 가져올 필요가 없다. Comment를 저장할 때, item의 id를 외례키로 저장하게 되는데, load()로 가져온 Proxy가 딱 그 id만 가지고 있기 때문이다.

Persistent 객체 수정하기

  • Persistent 상태의 객체는 tx.commit()을 호출할 때 flush()를 통해서 DB에 반영된다.
  • automatic dirty checking

Persistent 객체를 Transient 상태로 만들기

  • delete()를 호출하면 Remonved 상태가 된다.
  • DB와 동기화는 Unit of work가 끝날 때 이루어 지고, 그러면 Transient 상태가 된다.
  • 그런데.. id값을 가지고 있는데, transient 객체라고 할 수 있는건가.. detached 객체랑 차이가 뭘까?
    => Transient 상태가 될 때, 식별자도 제거하려면 hibernate.use_identifier_rollback 이 설정을 해줘야돼.
sessionFactory 설정
<prop key="hibernate.use_identifier_rollback">true</prop>
session = sessionFactory.openSession();
transaction = session.beginTransaction();

session.delete(member);

transaction.commit();
session.close();
assertNull(member.getId());

객체 복사하기

  • 좀 특별한 경우에 한쪽 DB에서 가져온 다음에 다른쪽 DB에 저장하는 경우가 있을 수 있습니다.
  • 서로 다른 SessionFactory에서 얻어온 Session 사이에서 detached 객체를 받아서 persistent 객체로 전환해 줍니다.
Session session = sessionFactory1.openSession();
Transaction tx = session.beginTransaction();
Item item = (Item) session.get(Item.class, new Long(1234));
tx.commit();
session.close();

Session session2 = sessionFactory2.openSession();
Transaction tx2 = session2.beginTransaction();
session2.replicate(item, ReplicationMode.LATEST_VERSION);
tx2.commit();
session2.close();
  • ReplicationMode
    1. ReplicationMode.IGNORE: 같은 id를 가진 녀석이 이미 존재하면, 그 객체를 넣지 않는다.
    2. ReplicationMode.OVERWRITE: 같은 id를 가진 녀석이 존재하면, 덮어쓴다.
    3. ReplicationMode.EXCEPTION: 같은 id를 가진 녀석이 존재하면, 예외를 던진다.
    4. ReplicationMode.LATEST_VERSION: 같은 id를 가진 녀석의 버전을 확인해서 새것 보다 이전 버전이면 덮어쓰고 아니면 넣지 않는다. 이 걸 사용하려면 하이버의 optimistic concurrency control을 켜둬야 한다.
top


Reattaching과 Merging

Hibernate/Chapter 9 : 2008.03.11 13:18


detached 상태의 객체를 persistent 객체로 전이할 때에는 Reattach가 간편하긴 하지만, 여러 개의 Session에 걸쳐 있을 경우에 예상치 못한 문제가 발생할 수 있습니다.

    @Test
    public void reattaching() {
        Session session1 = sessionFactory.openSession();
        Transaction transaction = session1.beginTransaction();
       
        Member member = new Member();
        member.setName("toby");
        session1.save(member);
       
        transaction.commit();
        session1.close();
       
       member.setName("whiteship");
       
        Session session2 = sessionFactory.openSession();
        Transaction transaction2 = session2.beginTransaction();
       
      Member member2 = (Member) session2.get(Member.class, member.getId());
      session2.update(member);
       
        assertEquals("whiteship", member.getName());
       
        transaction2.commit();
        session2.close();
    }

바로 이런 경우인데요. 위의 코드는 NonUniqueObjectException 예외가 발생합니다. 왜 그런지는 비밀입니다.
암튼 위의 코드야 둘이 붙어있으니까 왜 문제가 발생하는지 잘 보이지만, 여러 세션에 걸쳐서 detacked 객체를 붙였다 땟다 한다고 생각하면, 일일히 추적해서 update()를 사용한 줄을 위쪽으로 올려주는 작업을 해야 합니다. 고통이겠죠.

    @Test
    public void merging() {
        Session session1 = sessionFactory.openSession();
        Transaction transaction = session1.beginTransaction();
       
        Member member = new Member();
        member.setName("toby");
        session1.save(member);
       
        transaction.commit();
        session1.close();
       
        member.setName("whiteship");
       
        Session session2 = sessionFactory.openSession();
        Transaction transaction2 = session2.beginTransaction();
       
      Member member2 = (Member) session2.get(Member.class, member.getId());
      Member member3 = (Member) session2.merge(member);
       
        assertEquals("whiteship", member3.getName());
       
        transaction2.commit();
        session2.close();
    }

이럴 때 사용할 수 있는 것이 merge() 입니다. 이 녀석은 재밌게 동작합니다. 저렇게 하면 아무런 문제도 발생하지 않고 원하는 대로 동작합니다.

대신 주의 해야 할 것은 member2 와 member3가 같은 레퍼런스를 가지고 있기 때문에 아래와 같은 코드가 가능합니다.

        Member member2 = (Member) session2.get(Member.class, member.getId());
        Member member3 = (Member) session2.merge(member);
       
        member2.setName("whiteship2");
       
        assertEquals("whiteship2", member3.getName());

이런 상황이 용납이 안 되는 것은 아닙니다. 하지만 사방에서 저 객체의 값을 변경하면 좀 곤란하겠죠. member2와 member(detached 상태의 객체)는 merge()의 반환값을 가져온 이상. 사장되었다고 생각하고 작업을 하는 것이 좋겠습니다.

merget()와 Persistent Context의 dirty checking이 맞물리면, 정말 SQL이 재미있게 생성되는 모습을 볼 수 있습니다.

위의 예제에서 발생되는 쿼리는 간단합니다.
- member를 넣는 insert 문
- member2를 가져오는 select문
- member3을 가져올 때(merging time)member2가 참조하던 객체를 변경하는 update문


퀴즈 그렇다면 아래의 코드는 어떤 SQL들을 만들어 낼까요?

        Session session1 = sessionFactory.openSession();
        Transaction transaction = session1.beginTransaction();
       
        Member member = new Member();
        member.setName("toby");
        session1.save(member);
       
        transaction.commit();
        session1.close();
       
        member.setName("whiteship");
       
        Session session2 = sessionFactory.openSession();
        Transaction transaction2 = session2.beginTransaction();
       
        Member member2 = (Member) session2.get(Member.class, member.getId());
        Member member3 = (Member) session2.merge(member);
       
        member2.setName("toby");
       
        assertEquals("toby", member3.getName());
       
        transaction2.commit();
        session2.close();

위와 같이 간단하게 발생되는 쿼리 종류를 써주세요. 그리고 왜!! 왜 그런 결과가 생기는지도 설명해주세요~
top


Persistence context 확장하기

Hibernate/Chapter 9 : 2008.02.24 14:02


특징

  • 요청을 처리한 다음에 Persistent context를 닫지 않고 DB Connection을 끊은 상태에서 사용자의 다음 요청을 기다린다. 사용자의 다음 요청이 오면 다시 DB에 연결하고 다음 요청을 처리한다. Conversation이 끝나면, Persistent context를 DB에 동기화하고, 닫는다. 다음 Conversation을 시작할 때는 새로운 Persistence context를 시작하고, 이전 Conversation에서 사용했던 entity 객체들을 재사용하지 않는다.
  • Detached 상태의 객체가 없다. 모든 객체는 Transient 상태이거나 Persistent 상태다.
  • 손수 reattachment 하거나 merging할 필요가 없다.
  • 이 책의 나중에 두 가지 Conversation 구현 전략에 대해 자세하게 다룰 것이다.

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

Hibernate VS JPA  (4) 2008.03.11
하이버네이트 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
top


The identity of detached objects

Hibernate/Chapter 9 : 2008.02.24 14:02


특징

  • Reference to a detached object: 객체의 레퍼런스가 identity를 보장받는 범위를 벗어나는 것을 말한다.
  • Set Collection에 객체들을 담을 때, 동일한 주키로 읽어들인 Detached 상태의 객체와 Persistent 상태의 객체는 모두 동일한 레코드를 나타내는 것들이기 때문에, 이전 예제에서 다음의 코드의 결과 Set에는 하나의 객체만 들어가야 한다.
Set<Member> members = new HashSet<Member>();
members.add(loadedMember1);
members.add(loadedMember2);
members.add(loadedMember3);

assertEquals(1, members.size());
  • 그러나..2개가 들어가있다. loadedMember1과 loadedMember2는 같은 레퍼런스를 가지고 있으니까, loadedMember2는 추가되지 않고, loadedMember3는 전혀 다른 레퍼런스를 가지고 있기 때문에 추가 된다.
  • Set에 add()를 할 때, equals()로 비교를 하는데, Object 클래스에서 equals()를 ==으로 구현해 두었기 때문에 그렇게 된다.
  • 따라서 위의 결과가 1이 되게 하려면, equals()와 hashCode()를 재정의 해주어야 한다.

equals()와 hashCode() 이해하기

  • Detached 상태의 객체를 Set에 집어넣는 일이 절대로 없다면, 굳이 equals()와 hashCode()를 구현하지 않아도 된다.
  • Conversation 구현 전략으로 Extended Persistence Context를 선택하고 Detached 상태의 객체들을 애플리케이션에 제거하면 된다.
  • 이렇게 하면 당연히 Persistence Context도 Conversation으로 확장되었으니, Scope of Object identity의 범위도 Conversation으로 확장된다.
  • equlas()를 재정의 하면 반드시 hashCode()도 재정의 해야 한다. 왜? 같은 객체들은 반드시 같은 해시코드를 반환해야 하니까.

Data identity 비교하기

@Override
public boolean equals(Object other) {
if (this == other)
return true;
if (id == null)
return false;
if (!(other instanceof Member))
return false;
final Member that = (Member) other;
return this.id.equals(that.getId());
}

@Override
public int hashCode() {
return id == null ? System.identityHashCode(this) : id.hashCode();
}
  • 비추하는 방법니다.
  • Transient 객체를 Set에 넣은 다음에, Session.save()를 사용해서 Persistent 상태로 바꾸면 Set에 포함되어 있는 상태에서 hashCode 값이 바뀌게 된다. 이것은 Set의 제약을 위반하게 되는 것이다. 왜 그럴까? 생각해보자. 0이라는 hashCode를 가진 녀석이 Set에 들어간 다음에 1로 바꼈다. 그러면 나중에 1이라는 hashCode를 가진 녀석을 Set에 집어 넣으면 어떻게 될까? 처음에 0을 가지고 있다가 1로 바뀐 녀석은 사라지고, 뒤에 추가된 객체만 남아있을 것이다.(테스트 코드로 확인해 볼 것.)

주키 속성을 제외한 모든 속성을 비교하기

@Override
public boolean equals(Object other) {
if (this == other)
return true;
if (!(other instanceof Member))
return false;
final Member that = (Member) other;
if (!this.getName().equals(that.getName()))
return false;
return true;
}

@Override
public int hashCode() {
int result = 14;
result = 29 * result + getName().hashCode();
return result;
}
  • 콜렉션은 비교하지 않는다. 객체 하나의 동일성을 확인하려고 전체 객체 맵을 확인할 필요는 없으니까.
  • 단점1: 같은 레코드를 표현하는 두 개의 객체가 서로 다른 Session에서 존재하다가 누군가 속성을 바꾸면 그 둘은 다른 객체가 되버린다.
  • 단점2: 서로 다른 DB indentity를 가진 객체들이 같은 객체로 취급될 여지가 있다.

비즈니스 키를 사용해서 구현하기

  • business key: 각 개체들마다 유일한 값을 가지는 속성이나 속성들의 집합. natural primary key와 다른 점은 바뀔 수 있다는 것이다.
  • Entity 클래스에는 반드시 business key가 있어야 한다고 주장한다. 심지어 그 키가 개체의 모든 속성을 포함하는 것일지라도.(보통 immutable 클래스가 그렇다.)
  • surrogate key가 DB와 application이 사용하는 것이라면, business key는 사용자가 단일 레코드를 식별하기 위해 사용하는 것들이다.
@Override
public boolean equals(Object other) {
if (this == other)
return true;
if (!(other instanceof Member))
return false;
final Member that = (Member) other;
if (!this.email.equals(that.getEmail()))
return false;
return true;
}

@Override
public int hashCode() {
return email.hashCode();
}
  • 위에서 언급했던 모든 문제가 해결된다.
  • equals() 메소드에서 other객체의 속성에 접근할 때, .으로 바로 접근할 수 있는데도 굳이 gette를 사용하는 이유? 프록시 일수도 있기 때문에...

참조할 것

'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


The Scope of Object Identity

Hibernate/Chapter 9 : 2008.02.24 14:01


특징

  • Java identity: a==b
  • Database identity: x.getId().equals(y.getId())
  • Scope of object identity: Java Identity와 Database identity모두 보장되는 상태.

Scope of object identity 종류

  • No identity scope: 하나의 DB 레코드가 같은 자바 객체로 여러번 애플리케이션으로 반환되든지 말든지 신경 안 쓴다. 문제가 생긴다. (만약에 두 개의 객체가 하나의 레코드를 표현하고 있는데, 그 두 개의 객체의 정보를 수정하면, DB에는 어떤 객체의 변화를 반영해야 하는가? 어떻게 하든 둘 중 하나는 손실 되겠군.)
  • Persistence context-scoped identity: 단일 Persistence context 내부에서는 오직 하나의 객체만이 하나의 DB 레코드를 표현할 수 있도록 보장한다. 위에서 언급했던 문제가 없어지고 context 레벨에서의 cache를 보장할 수 있다.
  • Process-scoped identity: 한 단계 더 나가서, 전체 JVM 프로세스 내부에서 오직 하나의 객체만이 하나의 DB 레코드를 표현할 수 있다.
  • 하이버는 persistence context-scope으로 구현했다.

Persistence context-scope 예제

@Test
public void persistenceContextScope() throws Exception {
Session session = sessionFactory.openSession();
Member member = new Member();
session.save(member);
Long memberId = member.getId();

Member loadedMember1 = (Member) session.get(Member.class, memberId);
Member loadedMember2 = (Member) session.get(Member.class, memberId);

// Java Identity In Persistence Context
assertTrue(loadedMember1 == loadedMember2);
// DB Identity In Persistence Context
assertTrue(loadedMember1.getId().equals(loadedMember2.getId()));
session.flush();
session.close();

Session session2 = sessionFactory.openSession();
Member loadedMember3 = (Member) session2.get(Member.class, memberId);

// Java Identity Out Persistence Context
assertFalse(loadedMember1 == loadedMember3);

// DB Identity Out Persistence Context
assertTrue(loadedMember1.getId().equals(loadedMember3.getId()));

session2.flush();
session2.close();
}
  • Persistent Context 내에서는(Persistent 상태) 같은 주키로 가져온 객체는 Java identity와 DB identity 모두 동일하다.
  • 하지만 Detached 상태의 객체와 Persistence 상태의 객체의 Java Identity는 보장되지 않는다. 물론 이 때도 주키 속성은 가지고 있으니까 DB Identity는 보장된다.
  • Detached 상태의 객체를 가지고 작업하는 것는 위와 같이 Scope of Object identity가 보장되지 않는 상태에서 작업하는 것이다.

'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


Conversation 소개

Hibernate/Chapter 9 : 2008.02.24 13:59


특징

  • Persistence를 사용하는 여러 화면에 걸쳐 사용자의 요청을 처리해야 하는 경우.
  • 두 가지 구현 방법이 있다.

Detached Object를 이용하는 방법

  • session-per-request-with-detatched-objects
  • Persistent context는 오직 하나의 요청을 처리하는 동안만 지속되고, Detached 상태의 객체들은 Conversation 내부에서는 계속해서 reattach하거나 merge를 해서 다시 Persistent 상태로 만들어서 사용한다.

Persistence context를 확장하는 방법

  • session-per-conversation
  • Persistent context를 전체 작업의 단위로 확장시킨다.

'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


Persistence Context

Hibernate/Chapter 9 : 2008.02.24 13:58


특징

  • 관리하고 있는 엔티티 개체에 대한 캐시라고 생각할 수 있다.
  • 하이버네이트에서는 하나의 Session이 하나의 내부 Persistent Context를 가지고 있다고 한다.
  • JP에서는 하나의 EntityManager가 하나의 Persistent Context를 가지고 있다.
  • Persistent 상태의 모든 엔티티들은 이 컨텍스트 안에 캐시된다.
  • 이 녀석이 유용한 이유
  1. 하이버는 automatic dirty checking과 transactional write-behind를 할 수 있다.
  2. 하이버는 이 녀석을 1차 캐시로 사용할 수 있다.
  3. 하이버는 가바 객체 식별자의 스콥을 보장할 수 있다.
  4. 하이버는 Persistent Context를 전체 Conversation 범위로 확장할 수 있다.

Automatic dirty checking

  • Persistent 상태의 객체는 Unit of work가 끝날 때 SQL을 날려서 DB와 동기화 된다. 이 과정은 다른 때에 발생시킬 수도 있다.
  • 하이버는 쿼리를 실행하기 전에 DB와 동기화 할 수 있다. ??? 이것으로 인해 쿼리는 unit of work 초기에 변경 사항을 알 수 있다. ??
  • dirty: DB에 반영되지 않은 변경 사항.
  • transparent transaction-level write-behind를 사용해서 하이버는 변경 사항들을 쥐도 새도 모르게 가능한한 나중에 DB에 반영할 수 있다.
  • 변경 사항을 가능한한 나중(DB 트랜잭션 끄트머리)에 반영함으로써 롹 타임을 최소화 한다.
  • 하이버는 객체의 상태를 수정하는 UPDATE문을 날릴 때, SessionFactory를 생성하면서 만들어두었던 SQL을 사용하는데, 그 SQL에는 모든 필드를 포함하고 있다.
  • 하이버는 정확히 어떤 속성이 변경 됐는지 알아내고 해당 컬럼만 UPDATE 문을 사용하여 수정하는 것이 가능하다. 이렇게 함으로써, DB에 부하를 줄이는 대신 약간의 작업이 필요하니까 성능이 약간 떨어질 수도 있다.(DB에 부하를 주느니, 메모리랑 CPU를 조금 더 쓰는게 좋겠지.)
  • 새로운 레코드를 추가할 때도 위와 같은 메카니즘이 구현되어 있다.
  • dynamic-update="true", dynamic-insert="true"를 사용하여 설정할 수 있다.
@Entity
@org.hibernate.annotations.Entity(
dynamicInsert = true, dynamicUpdate = true
)
@Test
public void dirtyChecking() throws Exception {
Group group = new Group();
group.setName("KSUG");
session.save(group);

group.setName("AJN");
}
  • 이 설정들은 컬럼이 50개 정도로 매우 많을 때 설정하기를 권장한다.
  • 커스텀 dirty checking 알고리즘을 구현하여 사용할 수도 있다.

Persistence Context Cache

  • Caching의 또 다른 장점 repeatable read
  • 주키를 사용하여 객체를 로딩하라는 지시를 받으면, 하이버는 현재 Unit of work의 Persistent Context 안에서 먼저 찾아 본다. 그 안에 있으면 DB를 다녀오지 않고 그 녀석을 돌려준다.
@Test
public void repeatablRead() throws Exception {
Group group = new Group();
session.save(group);

Group group2 = (Group) session.get(Group.class, group.getId());

assertEquals(group, group2);
}
  • 캐시는 성능 향상과 isolation level을 공짜로 향상시켰다.
  • Persistent context가 불필요한 DB 트래픽을 줄이는데 도움을 주지만, 그 보다 더 중요한 것들이 있다.
  1. Persistent laver에서 객체 그래프 내의 circular reference로 인한 스택 오버플로우를 야기시키지 않는다.
  2. Unit of work의 종료 시점에 같은 DB 레코드를 나타내는 여러 객체가 있을 수 없다. 오직 하나의 객체만 하나의 레코드를 나타낼 수 있고, 그에 대한 변경 사항은 안전하게 DB에 반영된다.
  3. 특정 Persistent context 내에서 실행되는 코드들은 모두 해당 Persistent context 내부에 있는 객체을 참조할 수 있다. repeatable read를 보장한다.
  • 이런 기능을 가능캐 하는 persistence context cache는 별도의 조작이 필요없이 항상 가동되어 있으며, 끌 수도 없다.

'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


객체 상태

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


Persistence Lifecycle

Hibernate/Chapter 9 : 2008.02.24 13:55


  • Persistence lifecycle: 영속화 시켜야 할 객체의 상태와 라이프 사이클
  • Unit of work: 한 그룹으로 생각하는 Operation 집합.
  • Persistence Context: 특정 작업 단위에 있는 객체들에 대한 수정과 상태의 변화를 기억하는 캐쉬
  • 객체 상태
  • Persistence Context

'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