1. JPA의 지연 저장소와 트랜잭션 커밋
JPA는 SQL 실행을 최적화하기 위해 쓰기 지연 메커니즘을 사용한다. EntityManager.persist()를 호출해면 먼저 영속성 컨텍스트에 저장이 되고 그 뒤에 SQL문이 바로 실행되지 않고, 지연 저장소에 저장된다. SQL문은 트랜잭션 커밋 시점에 실행된다.
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("test");
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
transaction.begin(); // 트랜잭션 시작
try {
Tutor tutor = em.find(Tutor.class, 1L); // 엔티티 조회
tutor.setName("수정된 이름"); // 엔티티 상태 변경
// persist() 호출 없이도 변경 감지로 업데이트 SQL 실행
transaction.commit(); // 커밋 시점에 SQL 실행
} catch (Exception e) {
e.printStackTrace();
transaction.rollback(); // 실패 시 롤백
} finally {
em.close();
}
emf.close();
}
주요 포인트
- 커밋 시점에 JPA가 지연 저장소에 쌓인 내용을 데이터베이스에 반영
참고) IDENTITY 전략 사용시 persist() 호출 시 INSERT가 발생해 지연 저장소 활용이 제한된다.
2. 변경 감지(Dirty Checking)
JPA는 엔티티의 상태 변화를 자동으로 감지하는 변경 감지 기능을 제공한다.
트랜잭션이 시작되면, 영속성 컨텍스트는 엔티티의 초기 상태를 스냅샷(snapshot)으로 저장한다.
트랜잭션 커밋 시점에 스냅샷과 현재 상태를 비교해 변경된 내용을 감지하고 SQL을 생성함.
public static void main(String[] args) {
// EntityManagerFactory 생성
EntityManagerFactory emf = Persistence.createEntityManagerFactory("test");
// EntityManager 생성
EntityManager em = emf.createEntityManager();
// Transaction 생성
EntityTransaction transaction = em.getTransaction();
// 트랜잭션 시작
transaction.begin();
try {
Tutor tutor = em.find(Tutor.class, 1L);
tutor.setName("수정된 이름");
// Java Collection을 사용하면 값을 수정하고 다시 저장하지 않는다.
// em.persist(tutor);
System.out.println("트랜잭션 Commit 전");
// transaction이 commit되며 실제 SQL이 실행된다.
transaction.commit();
System.out.println("트랜잭션 Commit 후");
} catch (Exception e) {
// 실패 -> 롤백
e.printStackTrace();
transaction.rollback();
} finally {
// 엔티티 매니저 연결 종료
em.close();
}
emf.close();
}
- em.persist(tutor); 로 저장하지 않아도 updateSQL이 실행된다.
- Entity를 변경하고자 할 때 em.persist()를 사용하지 않아야 실수를 방지
내부동작
- 스냅샷(snapshot)

3. Flush와 Commit의 차이
Flush는 영속성 컨텍스트의 변경 내용을 데이터베이스에 반영하는 기능으로, 변경된 엔티티 정보를 SQL로 변환해 데이터베이스에 동기화 한다. 반면 Commit은 트랜잭션을 종료하며 변경 내용을 최종 확정한다.
트랜잭션 커밋 시 자동으로 실행되지만 특정 시점에 데이터베이스 반영이 필요할 때 수동으로 호출 할 수 있다.
- 자동 Flush
- 트랜잭션이 Commit되는 시점에 자동으로 호출
- 수동 Flush
- 필요한 시점에 em.flush()를 호출해 강제로 데이터베이스에 동기화할 수 있다.
public static void main(String[] args) {
// EntityManagerFactory 생성
EntityManagerFactory emf = Persistence.createEntityManagerFactory("test");
// EntityManager 생성
EntityManager em = emf.createEntityManager();
// Transaction 생성
EntityTransaction transaction = em.getTransaction();
// 트랜잭션 시작
transaction.begin();
try {
Tutor tutor = new Tutor(1L, "pbc", 100);
em.persist(tutor);
// flush 수동 호출
em.flush();
System.out.println("트랜잭션 Commit 전");
// transaction이 commit되며 실제 SQL이 실행된다.
transaction.commit();
} catch (Exception e) {
// 실패 -> 롤백
e.printStackTrace();
transaction.rollback();
} finally {
// 엔티티 매니저 연결 종료
em.close();
}
emf.close();
}
주요 포인트
- Commit은 Flush 이후 트랜잭션을 종료한다.
4. 실수를 방지하기 위한 팁
- 변경 감지 활용 : 엔티티 변경 후 별도로 persist()를 호출하지 않아도 된다. 잘못된 persist() 호출로 인해 불필요한 INSET SQL이 실행될 수 있으니 주의해야 한다.
- Flush 남용 주의 : Flush는 커밋 이전 시점에 데이터베이스 동기화가 필요한 경우에만 사용해야함.
- SQL 로그 확인 : Hibernate의 show_sql = true 설정을 활용하여 SQL 실행 시점을 확인
'Back-End > JPA' 카테고리의 다른 글
| JPA 연관관계 Mapping (0) | 2024.12.10 |
|---|---|
| JPA - Entity 만들기 (0) | 2024.12.10 |
| JPA - 영속성 컨텍스트(1차 캐시, 동일성 보장, 쓰기 지연) (0) | 2024.12.10 |
| JPA - 영속성 컨텍스트(Entity) (0) | 2024.12.10 |
| JPA에 대해서 (2) | 2024.12.10 |