참고 : 래퍼런스 10장

객체 영속화 하기(저장 하기)
새로 생성된 객체는 Session.save(), Session.persist(), Session.saveOrUpdate() 메서드를 통해 영속화 할 수 있습니다. 연관된 객체가 있을 경우 cascade 설정에 따라 같이 영속화 될 수 있습니다.
Member member = new Member();
member.setName("jjaeko");
session.save(member);
save()는 식별 값을 리턴하며 persist()는 식별값을 리턴하지 않는 차이점이 있습니다.
saveOrUpdate()는 자동적인 상태 검출 에서 다루겠습니다.


객체 로드하기
데이터베이스에 저장된 객체는 Session.get(), Session.load() 메서드를 이용해 로드할 수 있습니다.
Member member = (Member)session.get(Member.class, 1);
Member member2 = (Member)session.load(Member.class, 2);
Session.get() 메서드는 즉시 객체를 가져오는 반면 Session.load() 메서드는 인자로 전달된 식별 값을 가지는 프록시 객체를 리턴하는 차이점이 있습니다. 리턴된 프록시 객체는 사용하는 시점에서 비로소 저장된 값을 가져 옵니다. 만약 데이터베이스에 식별 값에 해당하는 객체가 없다면 ObjectNotFoundException이 던져 집니다. 따라서 객체가 존재하는지 확신하지 않는다면 Session.get() 메서드를 사용해야 합니다. Session.get() 메서드는 객체가 존재하지 않을경우 null을 리턴 합니다.


영속 객체 변경하기
영속 객체를 변경하고 이를 반영하기 위해 별도의 메서드를 호출할 필요가 없습니다. 더티체킹 기능이 있기 때문인데 객체의 변경을 알아채고 자동으로 동기화 하는 기능입니다.
Member member = (Member)session.get(Member.class, 1);
member.setName("째코");
동기화 시점은 컴밋 되거나 flush() 될 때 입니다. 만약 컴밋이나 flush()가 되지 않는다면 동기화도 되지 않습니다. flush()에 대해서는 즉시 동기화 하기 에서 다루겠습니다.


준영속 객체 변경하기
Session이 종료되면 영속 객체는 준영속 객체로 변경 됩니다. 준영속 객체는 Session.update(), Session.merge(), Session.replicate() 메서드를 통해 재첨부 될 수 있습니다.

Session.update() 메서드를 이용한 예
Session이 종료된 후 준영속 객체를 새로운 Session의 update() 메서드를 통해 영속 상태로 바꿀수 있습니다. update() 메서드를 호출하고 컴밋이나 flush()가 되지 않는다면 Update 쿼리가 수행되지 않습니다. 그리고 객체의 변경 유무를 떠나 Update 쿼리가 실행되는데 이를 방지하려면 select-before-update 속성 값을 true로 설정하면 됩니다.
Member member = (Member)session.get(Member.class, 1);
session.close();                     // Session 종료
member.setName("김재호"); // Session 종료후 값 변경
session2.update(member);   // 새로운 Session

Session.merge() 메서드를 이용한 예

update() 메서드를 이용할 때 한가지 문제점이 있습니다. 새로운 Session내에 update()하려는 준영속 객체와 동일한 식별 값을 갖는 영속 객체가 존재한다면 NonUniqueObjectException이 던져 집니다.
Member member = (Member)session.get(Member.class, 1);
session.close();                    
member.setName("김재호");
Member member2 = (Member)session.get(Member.class, 1);
session2.update(member);   // 예외 발생
이 경우 merge() 메서드를 사용 합니다. merge() 메서드는 전달된 준영속 객체의 값을 동일한 식별 값을 갖는 객체에 적용한뒤 리턴 합니다. 그리고 전달된 준영속 객체는 계속 준영속 상태로 남아 있습니다.
Member member = (Member)session.get(Member.class, 1);
session.close();                    
member.setName("김재호");
Member member2 = (Member)session.get(Member.class, 1);
Member member3 = session2.merge(member);   // member2와 member3은 같은 객체
위 예제에서는 merge() 메서드를 호출하기 전 이미 동일한 식별 값을 갖는 영속 객체가 존재 합니다. 만약 동일한 식별 값을 갖는 영속 객체가 존재하지 않을 경우 알아서 로드후 변경을 적용 합니다.


자동적인 상태 검출

대상 객체가 새로운 객체인지 아니면 준영속 객체인지에 따라 save() 메서드나 update() 메서드를 구분하여 호출 해야 합니다. 하지만 Session.saveOrUpdate() 메서드는 객체의 상태를 파악하고 알아서 작업을 수행 합니다. Session.saveOrUpdate() 는 대략 이런 시나리오에 의해 사용됩니다.
1. 첫번째 Session에서 객체 로드
2. 로드된 객체가 UI로 전달
3. UI에서 객체 변경
4. 두번째 Session에서 update()
saveOrUpdate() 메서드의 행동은 이렇습니다.
  • 대상 객체가 현재 Session 내에 영속화 되어 있을 경우 아무 작업도 하지 않습니다.
  • Session 내에 전달된 준영속 객체와 동일한 식별 값을 갖는 객체가 존재한다면 예외가 던져 집니다.
  • 대상 객체가 식별자 프로퍼티를 갖고 있지 않다면 save() 합니다.
  • 대상 객체가 식별 값을 갖고 있지 않다면 save() 합니다.
  • version 값이 없다면 save() 합니다.
  • 그 밖의 경우 update() 합니다.


영속 객체 삭제하기
Session.delete() 메서드를 이용해 데이터베이스에 저장된 객체를 삭제할 수 있습니다. 객체를 삭제하더라도 어플리케이션 에서는 여전히 준영속 상태로 객체를 참조할 수 있습니다.


서로 다른 데이터베이스 사이에 객체 복사하기
Session.replicate() 메서드를 이용해 서로 다른 데이터베이스 사이에 객체를 복사할 수 있습니다.
(아래 예제 코드에서 트랜잭션 코드는 생략 했습니다.)
Member member = session1.get(Member.class, 1);
session1.close();
session2.replicate(member, ReplicationMode.IGNORE);
replicate() 메서드의 두번째 인자는 대상 데이터베이스에 동일한 식별 값을 갖는 객체가 이미 있을 경우 처리 방법을 결정 합니다. 다음 4가지 옵션이 있습니다.
ReplicationMode.IGNORE : 실행을 무시합니다.
ReplicationMode.OVERWRITE : 덮어 씌웁니다.
ReplicationMode.EXCEPTION : 예외를 발생 시킵니다.
ReplicationMode.LASTED_VERSION : 마지막 버전 값을 갖는 객체를 선택 합니다.
그리고 세가지 주의 사항이 있습니다.
첫번째로 복사하려는 객체는 반드시 준영속 객체여야 합니다.
두번째로 복사하려는 객체가 연관 객체가 있을 경우 cascade속성 값에 replicate가 지정되어 있어야 합니다.
세번째로 복사하려는 객체의 연관 객체가 프록시 객체라면 반드시 연관 객체들을 가져온 상태여야 합니다. 왜냐면 자신이 속해 있던 Session이 이미 종료되었기 때문에 연관 객체를 불러오지 못하고 대상 데이터베이스에 복사하지 못하기 때문 입니다.


즉시 동기화 하기
Session내에 존재하는 영속 객체는 기본적으로 다음 세가지 시점에서 데이터베이스와 동기화 됩니다.
org.hibernate.Transaction.commit() 호출
Session.flush() 호출
몇몇 쿼리가 실행되기 전
마지막 특정 쿼리가 실행되기 전의 경우에 대해 알아보겠습니다.
Member member = new Member();
session.save(member);
List memberList = session.createCriteria(Member.class).list();
새로 생성된 member 객체를 저장했지만 데이터베이스와 동기화되지 않은 상태입니다. 이 때 list() 메서드로 저장된 객체들을 조회할 경우 앞서 저장했던 member 객체는 어떻게 될까요? 아직 저장되지 않았기 때문에 누락될 것이지만 Hibernate는 이 경우 올바른 데이터 보장을 위해 member 객체를 동기화 합니다.
또 다른 예외 사항으로 native 식별자 생성기를 사용하는 객체를 저장할 경우 식별 값을 알기 위해 즉시 동기화 합니다. FlushMode에 대해서는 테스트 결과 뜻대로 되지 않아서 다루지 않겠습니다.

'Hibernate' 카테고리의 다른 글

동시 접근 제어  (0) 2008.03.29
openSession()과 currentSession()의 close()  (0) 2008.03.04
Session으로 작업하기  (0) 2008.03.02
객체의 상태  (0) 2008.03.02
상속 매핑  (0) 2008.02.23
m-n 연관 매핑, inverse 속성  (2) 2008.02.18
Posted by 째코

댓글을 달아 주세요