Reattaching과 Merging
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들을 만들어 낼까요?
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();
위와 같이 간단하게 발생되는 쿼리 종류를 써주세요. 그리고 왜!! 왜 그런 결과가 생기는지도 설명해주세요~