플러시는 영속성 컨텍스트의 변경 내용을 데이터베이스에 반영한다.
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 프로그래밍 책