Open jowoohyeong opened 1 year ago
애플리케이션과 DB사이에 왜 중간에 영속성 컨텍스트가 있냐, 왜 필요하냐, 아래와 같은 개념들이 가능하려면, 영속성 컨텍스트가 존재해야한다.
Member member = new Meber("member1", "회원1");
//1차 캐시에 저장
em.persist(member);
// 1차 캐시에서 조회
Member findMember = em.find(Member.class, "Member1");
Member a = em.find(Member.class, "Member1");
Member b = em.find(Member.class, "Member1");
Systen.out.println(a == b); // 동일성 비교 true
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
// 엔티티 매니저는 데이터 변경시 트랜잭션을 시작해야 한다.
transaction.begin(); // 트랜잭션 시작
em.persist(memberA);
em.persist(memberB);
// 이때까지 INSERT SQL을 데이터베이스에 보내지 않는다.
// 커밋하는 순간 데이터베이스에 INSERT SQL을 보낸다.
transaction.commit(); // 트랜잭션 커밋
<property name="hibernate.jdbc.batch_size" value=10/>
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
transaction.begin(); // 트랜잭션 시작
// 영속 엔티티 조회
Member memberA = em.find(Member.class, "memberA");
// 영속 엔티티 수정
memberA.setUsername("nj");
memberA.setAge(27);
//em.update(member) 또는 em.persist(member)로 다시 저장해야 하지 않을까?
transaction.commit(); // 트랜잭션 커밋
Member memberA = em.find(Member.class, "memberA");
em.remove(memberA); // 엔티티 삭제
// 영속
Member member = new Member(200L, "A");
em.persist(member);
em.flush();
System.out.println("플러시 직접 호출하면 쿼리가 커밋 전 플러시 호출 시점에 나감");
transaction.commit();
em.persist(member1);
em.persist(member2);
em.persist(member3);
// 중간에 JPQL 실행
query = em.createQuery("select m from Member m", Member.class);
List
- 플러시가 일어나면 1차 캐시가 삭제될까?
- 삭제되지 않는다. 쓰기 지연 SQL 저장소에 있는 쿼리들만 DB에 전송되고 1차 캐시는 남아있다.
### 플러시 모드 옵션
- em.setFlushMode(FlushModeType.COMMIT);
- FlushModeType.AUTO
- 커밋이나 쿼리를 실행할 때 플러시(기본값)
- FlushModeType.COMMIT
- 커밋할 때만 플러시
### 플러시 정리
- 플러시는 영속성 컨텍스트를 비우지 않는다.
- 플러시는 영속성 컨텍스트의 변경 내용을 데이터베이스에 동기화 한다.
- 플러시가 동작할 수 있는 이유는 데이터베이스 트랜잭션이라는 작업 단위(개념)가 있기 때문이다
- 어쨌든 트랜잭션이 시작되고 커밋되는 시점에만 동기화 해주면 되기 때문에, 그 사이에서 플러시 매커니즘의 동작이 가능한 것이다.
- JPA는 기본적으로 데이터를 맞추거나 동시성에 관련된 것들은 데이터베이스 트랜잭션에 위임한다.
준영속상태
------------
-영속 상태
- 영속성 컨텍스트의 1차 캐시에 올라간 상태가 영속 상태이다. 엔티티 매니저가 관리하는 상태
- em.persist()로 영속성 컨텍스트에 저장한 상태도 영속 상태이지만, em.find()로 조회할 때 영속성 컨텍스트 1차 캐시에 없어서 DB에 조회해와서 1차 캐시에 저장한 상태도 영속 상태이다.
- 코드로 보면
- em.find()가 일어날 때, 1차 캐시에 없으므로 DB에서 조회한 엔티티를 1차 캐시에 넣는다. 영속상태가 된다.
- setName으로 이름을 바꾸고 커밋하니까, Dirty Checking이 일어나서 1차 캐시의 엔티티와 스냅샷이 다른 것을 감지하고 UPDATE 쿼리가 날라간다.
```java
Member member = em.find(Member.class, 150L);
member.setName("AAAAA");
transaction.commit();
준영속상태 : 영속 상태의 엔티티가 영속성 컨텍스트에서 분리된 상태(detached)
em.detach(member); 로 멤버를 영속성 컨텍스트에서 분리하고 트랜잭션을 커밋하면, 아무 일도 일어나지 않는다.
JPA가 관리하지 않는 상태
아래 코드는 영속성 컨텍스트가 제공하는 기능을 사용하지 못해서 UPDATE 쿼리는 나가지 않는다.
Member member = em.find(Member.class, 150L);
member.setName("AAAAA");
em.detach(member);
transaction.commit();
준영속 상태로 만드는 방법
em.clear() - 영속성 컨텍스트를 완전히 초기화
아래 코드의 경우 첫번째 find에서 멤버를 SELECT해서 영속성 컨텍스트에 저장하고 clear() 했다.
그러고나서 같은 아이디를 가지는 멤버를 다시 조회하면 SELECT 쿼리가 다시 나간다.
총 2번의 SELECT 쿼리가 발생한다.
clear는 테스트 케이스 작성시에 도움이 된다.
Member member = em.find(Member.class, 150L);
member.setName("AAAAA");
em.clear();
Member member1 = em.find(Member.class, 150L);
transaction.commit();
em.close() 영속성 컨텍스트를 종료
영속성 컨텍스트 Persistence Context
JPA를 공부할 때 가장 중요한게 객체와 관계형 데이터베이스를 매핑하는 것(Object Relational Mapping)과 영속성 컨텍스트를 이해하는 것이다.