하이버네이트 API: 저장하고 읽어들이기
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
- ReplicationMode.IGNORE: 같은 id를 가진 녀석이 이미 존재하면, 그 객체를 넣지 않는다.
- ReplicationMode.OVERWRITE: 같은 id를 가진 녀석이 존재하면, 덮어쓴다.
- ReplicationMode.EXCEPTION: 같은 id를 가진 녀석이 존재하면, 예외를 던진다.
- ReplicationMode.LATEST_VERSION:
같은 id를 가진 녀석의 버전을 확인해서 새것 보다 이전 버전이면 덮어쓰고 아니면 넣지 않는다. 이 걸 사용하려면 하이버의
optimistic concurrency control을 켜둬야 한다.