플러시는 영속성 컨텍스트의 변경 내용을 데이터베이스에 반영한다.
em.flush() 직접 호출
트랜잭션 커밋 시 플러시 자동 호출
JPQL 쿼리 실행 시 플러시 자동 호출
JPQL이나 Criteria 같은 객체 지향 쿼리를 호출 시에 자동 호출
Why?
// 예제 3.6) JPQL 쿼리 실행 코드 예시
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();
<1> em.persist() 호출
-> 엔티티 memberA, memberB, memberC 영속상태로 만듦
-> 이들은 영속성 컨텍스트에 있지만 데이터베이스에 반영 X
<2> JPQL 실행
-> JPQL은 SQL로 변환되어 데이터베이스에서 엔티티 조회
-> but, 엔티티들은 아직 데이터베이스에 없어서 쿼리결과 조회 X
따라서 쿼리 실행 직전, 영속성 컨텍스트를 플러시하여 변경 내용을 데이터베이스에 반영해야 함
-> JPA는 JPQL을 실행할 때도 플러시를 자동 호출
참고로, find() 메소드 호출 시에는 플러시 실행 X
엔티티 매니저에 플러시 모드를 직접 지정하려면
javax.persistence.FlushModeType
사용.
FlushModeType.AUTO
: 커밋, 쿼리를 실행 시 플러시 (default)FlushModeType.COMMIT
: 커밋 실행 시에만 플러시 (성능 최적화 위해 사용)em.setFlushMode(FlushModeType.COMMIT) // 플러시 모드 직접 설정
플러시는 영속성 컨텍스트에 보관된 엔티티를 지우는 것 X
플러시는 영속성 컨텍스트의 변경 내용을 데이터베이스에 동기화하는 것 O
영속 -> 준영속의 상태 변화에 대해 알아보자
// 예제 3.7) detach() 메소드 정의
public void detach(Object entity);
// 예제 3.8) detach() 테스트 코드
public void testDetached() {
...
// 회원 엔티티 생성, 비영속 상태
Member member = new Member();
member.setId("memberA");
member.setUsername("회원A");
// 회원 엔티티 영속 상태
em.persist(member);
// 회원 엔티티를 영속성 컨텍스트에서 분리, 준영속 상태
em.detach(member);
transaction.commit(); // 트랜잭션 커밋
}
영속성 컨텍스트에서 memberA에 대한 모든 정보를 삭제함
// 예제 3.9) 영속성 컨텍스트 초기화
// 엔티티 조회, 영속 상태
Member member = em.find(Member.class, "memberA");
em.clear(); // 영속성 컨텍스트 초기화;
// 준영속 상태
member.setUsername("changeName");
transaction.commit(); // 트랜잭션 커밋
영속성 컨텍스트에 있는 모든 것이 초기화됨
영속성 컨텍스트 제거하고 새로 만든 것과 같음
준영속 상태로 변경 감지 동작 X -> 데이터베이스 반영 X
// 예제 3.10) 영속성 컨텍스트 닫기
public void closeEntityManager() {
EntityManagerFactory emf =
Persistence.createEntityManagerFactory("jpabook");
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
transaction.begin(); // [트랜잭션] - 시작
Member memberA = em.find(Member.class, "memberA");
Member memberB = em.find(Member.class, "memberB");
transaction.commit(); // [트랜잭션] - 커밋
em.close(); // 영속성 컨텍스트 닫기 (종료)
참고로, 영속 상태의 엔티티는 주로 영속성 컨텍스트가 종료되면서 준영속 상태가 됨.
개발자가 직접 준영속 상태 만드는 일 드뭄.
준영속 상태가 된 엔티티는 어떻게 되는 걸까?
준영속 -> 영속 상태로 변경하는 방법
merge() 메소드는 준영속 상태의 엔티티를 받아 그 정보로 새로운 영속 상태의 엔티티를 반환.
// 예제 3.11) merge() 메소드 정의
public <T> T merge(T entity);
// 예제 3.12) merge() 사용 예
Member mergeMember = em.merge(member);
// 예제 3.13) 준영속 병합 예제
public class ExamMergeMain {
static EntityManagerFactory emf =
Persistence.createEntityManagerFactory("jpabook");
public static void main(String args[]) {
/**
1. member 엔티티는 createMember() 메소드의 영속성 컨텍스트1에서 영속상태
-> 영속성 컨텍스트1 종료되면서 준영속 상태
-> createMEmeber() 메소드는 준영속 상태의 member 엔티티 반환
**/
Member member = createMember("memberA", "회원1");
/**
2. 준영속 상태에서 변경
-> but, member 엔티티를 관리하는 영속성 컨텍스트가 존재 X
-> 수정 사항 데이터베이스에 반영 X
**/
member.setUsername("회원명변경");
/**
3. 병합 사용하여 준영속 상태의 엔티티 수정
-> 새로운 영속성 컨텍스트2 시작
-> merge 호출
-> member 엔티티는 영속성 컨텍스트2가 관리하는 영속 상태로 변경
-> 트랜잭션 커밋 시 수정했던 회원명 데이터베이스에 반영
-> 참고로, mergeMember라는 새로운 영속 상태의 엔티티가 반환되는 것.
**/
mergeMember(member);
}
static Member createMember(String id, String username) {
//==영속성 컨텍스트1 시작==//
EntityManager em1 = emf.createEntityManager();
EntityTransaction tx1 = em1.getTransaction();
tx1.begin();
Member member = new Member();
member.setId(id);
member.setUsername(username);
em1.persist(member);
tx1.commit();
em1.close(); // 영속성 컨텍스트1 종료,
// member 엔티티는 준영속 상태가 됨
//==영속성 컨텍스트1 종료==//
return member;
}
static void mergeMember(Member member) {
//==영속성 컨텍스트2 시작==//
EntityManager em2 = emf.createEntityManager();
EntityTransaction tx2 = em2.getTransaction();
tx2.begin();
Member merMember = em2.merge(member);
tx2.commit();
// 준영속 상태
System.out.println("member = " + member.getUsername());
// 영속 상태
System.out.println("mergeMember = " + mergeMember.getUsername());
// em.contains(entity)
// : 영속성 컨텍스트가 파라미터로 넘어온 엔티티를 관리하는지 확인하는 메소드
// member(준영속) != mergeMember(영속)
System.out.println("em2 contatins member = " +
em2.contatins(member));
System.out.println("em2 contatins member = " +
em2.contatins(mergeMember));
em2.close();
//==영속성 컨텍스트2 종료==//
}
}
[출력 결과]
[merge() 동작 방식]
- merge()는 파라미터로 넘어온 준영속 엔티티를 사용하여
새롭게 병합된 영속 상태의 엔티티(mergeMember)를 반환함.
- 파라미터로 넘어온 엔티티(member)는 병합 후에도 준영속 상태
준영속 엔티티를 참조하던 변수를 영속 엔티티를 참조하도록 변경하는 것이 안전
// Member mergeMember = em2.merge(member)
member = em2.merge(member);
비영속 -> 영속 상태
Member member = new Member();
Member newMember = em.merge(member); // 비영속 병합
tx.commit();
새로운 엔티티를 생성하여 병합
병합은 준영속, 비영속을 신경 쓰지 않는다.
병합은 save or update 기능을 수행.
엔티티 매니저와 영속성 컨텍스트는 매핑한 엔티티를 실제 사용하는 동적인 부분.
출처 : 자바 ORM 표준 JPA 프로그래밍 책