영속성 컨텍스트로 JPA가 내부적으로 돌아가는 구조를 알아보자
영속성 컨텍스트를 공부하기 전 잠깐 엔티티 매니저에 대해 알아보자
웹 애플리케이션 개발시 Entity Manager Factory
를 통해 고객의 요청이 들어올 때마다 Entity Manager
를 생성한다
Entity Manager
는 내부적으로 데이터베이스 커넥션을 이용해서 데이터베이스를 사용한다.
그럼 영속성 컨텍스트는 뭐지?
EntityManager.persist
는 영속성 컨텍스트에 저장한다는 의미다 (db저장 x)엔티티에는 생명주기가 있다
이에 대해서 자세히 알아보자
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
try {
//이제 작업을 할 부분
} catch (Exception e) {
tx.rollback();
System.out.println("err : " + e);
} finally {
em.close();
emf.close();
}
}
Member member = new Member();
member.setId(100L);
member.setName("Hello JPA");
엔티티 매니저 안의 영속성 컨텍스트 안에서 관리되는 상태
객체 아래에 아래와 같은 코드를 추가해본다
System.out.println("===BEFORE===");
em.persist(member);
System.out.println("===AFTER===");
===BEFORE===
===AFTER===
Hibernate:
/* insert hellojpa.Member
*/ insert
into
Member
(name, id)
values
(?, ?)
EntityManager.persist
를 함em.persist
를 할 때 쿼리가 날라간 것이 아니다em.persist
를 입력하면 영속성컨텍스트에 쿼리문이 저장되는것이다tx.commit
을 입력해야 영속성 컨텍스트에 저장되어 있던 쿼리문이 DB에 전송되서 실행되는 것이다.detach
를 하면 영속성 컨텍스트에서 지워진다remove
를 하면 db에서 삭제된다영속성 컨텍스트 내부에는 1차캐시
라는 것을 가지고 있다.
select를 하기 전에 영속성컨텍스트에서 1차캐시를 조회한다음 값을 가져온다
아래에 다음의 코드를 추가해보고 실행해보자
//비영속 상태
Member member = new Member();
member.setId("member1");
member.setUsername("회원1");
//엔티티를 영속, 1차 캐시에 저장됨
em.persist(member);
//1차 캐시에서 조회
Member findMember = em.find(Member.class, 100L);
System.out.println("findMember.id = " + findMember.getId());
System.out.println("findMember.getName() = " + findMember.getName());
===BEFORE===
===AFTER===
findMember.id = 100
findMember.getName() = Hello JPA
Hibernate:
/* insert hellojpa.Member
*/ insert
into
Member
(name, id)
values
(?, ?)
EntityManager(영속 컨텍스트)
안에는 1차캐시가 있다.
1차캐시 안에는 id와 entity가 있다
id는 member의 아이디
entity는 entity의 값, member 객체가 담긴다
우선 캐시에서 찾은 후 캐시에 없으면 DB에서 조회를 한다
DB조회를 하면 다시 1차캐시에 저장하게 된다.
트랜젝션이 끝나면 같이 종료가 된다
Member a
Member b
Member a = em.find(Member.class, "member1");
Member b = em.find(Member.class, "member1");
System.out.println(a == b); //동일성 비교 true
Member a
) 에는 DB에서 조회 후 1차캐시에 저장된다Member b
) 에는 1차 캐시에서 가져온다
Member member1 = new Member(150L,"A");
Member member2 = new Member(160L,"B");
em.persist(member1);
em.persist(member2);
System.out.println("=====================");
tx.commit();
결과를 보면 commit을 한 순간에 쿼리문이 나가는 것을 확인할 수 있다.
영속성컨텍스트에는 1차캐시 외에도 쓰기지연 SQL 저장소라는 곳이 있다. 실행될 SQL문들이 저장되는 공간.
persist
를 하면 1차캐시에 저장되면서 쓰기지연 SQL 저장소에서 실행될 SQL 문이 저장된다.em.persist
를 한다고 해서 DB에 저장되는 것이 아니다.tx.commit
하는 순간 flush되고 쿼리문이 실행되어 commit이 된다.DB에 한번에 보낼 수 있다.
<property name="hibernate.jdbc.batch_size" value="10"/>
를 입력하면 기다렸다가 10개를 한번에 보낼 수 있다.
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
transaction.begin(); // [트랜잭션] 시작
// 영속 엔티티 조회
Member memberA = em.find(Member.class, "memberA");
// 영속 엔티티 데이터 수정
memberA.setUsername("hi");
memberA.setAge(10);
//[드랜잭션] 커밋
transaction.commit();
em.update(member)
가 없을까?em.persist(member)
는 필요 없을까?Member memberA = em.find(Member.class, “memberA");
em.remove(memberA); //엔티티 삭제
Member member = new Member(200L, "member200");
em.persist(member);
em.flush();
System.out.println("=========");
tx.commit();
em.persist(memberA);
em.persist(memberB);
em.persist(memberC);
//중간에 JPQL 실행
query = em.createQuery("select m from Member m", Member.class);
List<Member> members= query.getResultList();
em.setFlushMode(FlushModeType.COMMIT)
FlushModeType.AUTO
FlushModeType.COMMIT
em.detach(entity)
em.clear
em.close
개인 git에 가면 전체 코드를 확인 할 수 있습니다