JPA에서 가장 중요한 2가지
영속성 컨텍스트
엔티티의 생명주기
- 비영속 (new/transient)
// 객체를 생성한 상태(비영속) Member member = new Member(); member.setId("memver1"); member.setUsername("회원1");
- 영속 (managed)
EntityManager em = emf.createEntityManager(); em.getTransaction().begin(); // 객체를 저장한 상태(영속) em.persist(member);
- 준영속 (detached)
// 회원 엔티티를 영속성 컨텍스트에서 분리, 준영속 상태 em.detach(member);
- 삭제 (removed)
// 객체를 삭제한 상태(삭제) em.remove(member);
영속성 컨텍스트의 이점
- 1차 캐시
// 엔티티를 생성한 상태 Member member = new Member(); member.setId("member1"); member.setUsername("회원1"); // 1차 캐시에 저장됨 em.persist(member); // 1차 캐시에서 조회 Member findMember = em.find(Member.class, "member1"); // 데이터베이스에서 조회 Member findMember2 = em.find(Member.class, "member2");
- 동일성(identity) 보장
Member a = em.find(Member.class, "member1"); Member b = em.find(Member.class, "member1"); System.out.println(a == b); //동일성 비교 true
- 트랜잭션을 지원하는 쓰기 지연(transactional write-behind)
EntityManager em = emf.createEntityManager(); EntityTransaction transaction = em.getTransaction(); //엔티티 매니저는 데이터 변경시 트랜잭션을 시작해야 한다. transaction.begin(); // [트랜잭션] 시작 em.persist(memberA); em.persist(memberB); //여기까지 INSERT SQL을 데이터베이스에 보내지 않는다. //커밋하는 순간 데이터베이스에 INSERT SQL을 보낸다. transaction.commit(); // [트랜잭션] 커밋
- 변경 감지(Dirty Checking)
영속성 컨텍스트에 들어왔을 때의 상태를 스냅샷
커밋되는 시점에 엔티티와 스냅샷 비교해서 Update SQL 생성EntityManager em = emf.createEntityManager(); EntityTransaction transaction = em.getTransaction(); transaction.begin(); // [트랜잭션] 시작 // 영속 엔티티 조회 Member memberA = em.find(Member.class, "memberA"); // 영속 엔티티 데이터 수정 memberA.setUsername("hi"); memberA.setAge(10); //em.update(member) 이런 코드가 있어야 하지 않을까? transaction.commit(); // [트랜잭션] 커밋
영속성 컨텍스트의 변경 내용을 데이터베이스에 반영
플러시 발생
영속 → 준영속
영속 상태의 엔티티가 영속성 컨텍스트에서 분리(detached)
영속성 컨텍스트 제공 기능 사용 불가
JPA에서 관리하지 않음
// 영속 Member member = em.find(Member.class, 150L); member.setName("AAAAA"); // 준영속 em.detach(member);
트랜잭션 커밋해도 아무 일도 일어나지 않음
준영속 상태로 만드는 방법
em.detach(entity)
: 특정 엔티티만 준영속 상태로 전환em.clear()
: 영속성 컨텍스트를 완전히 초기화em.close()
: 영속성 컨텍스트를 종료