김영한 님의 "자바 ORM 표준 JPA 프로그래밍" 책을 정리한 포스팅 입니다.
1. 벌크 연산
- 수백개의 엔티티 수정을 하기 위해 각각 상태를 변경할 경우 너무 오래걸림
- 이를 위해 여러 건을 한번에 처리하는 벌크 연산을 지원함
예제)
더보기
@Test
@Transactional
void bulk_operation() {
String selectJpql = "select u from User as u";
List<User> result = entityManager.createQuery(selectJpql, User.class).getResultList();
long start1 = System.currentTimeMillis();
for (User user : result) user.setName("updated");
entityManager.flush();
entityManager.clear();
long end1 = System.currentTimeMillis();
System.out.println("일반 방식 소요 시간: " + (end1 - start1) + "ms");
long start2 = System.currentTimeMillis();
String updateJpql = "UPDATE User u SET u.name = CONCAT(u.name, '_bulk')";
int updatedCount = entityManager.createQuery(updateJpql).executeUpdate();
entityManager.clear();
long end2 = System.currentTimeMillis();
System.out.println("벌크 방식 소요 시간: " + (end2 - start2) + "ms");
}
일반 방식 소요 시간: 1471ms
벌크 방식 소요 시간: 103ms
- 100명의 유저가 있다면, 영속성 컨텍스트가 전부 관리 → 성능 부담
- 100건 각각 dirty checking 및 update query 요청 → 성능 낮음
주의점
- 벌크 연산은 데이터베이스에 직접 쿼리함 (영속성 컨텍스트와 연관 ❌)
- 벌크 연산 직후 영속성 컨텍스트에서 가져온 데이터는 1차 캐시에 속한 데이터
- DB에 수정된 값이 반영되지 않음
예제) 해결 방법
더보기
refresh()
@Test
@Transactional
void bulk_operation_solution_refresh() {
User user = entityManager.find(User.class, 1);
String updateJpql = "UPDATE User u SET u.age = 100";
entityManager.createQuery(updateJpql).executeUpdate();
assertNotEquals(user.getAge(), 100);
entityManager.refresh(user);
assertEquals(user.getAge(), 100);
}
벌크 연산 수행 후 영속성 컨텍스트 초기화
@Test
@Transactional
void bulk_operation_solution_persistence_context_clear() {
String updateJpql = "UPDATE User u SET u.age = 100";
entityManager.createQuery(updateJpql).executeUpdate();
entityManager.clear();
User user = entityManager.find(User.class, 1);
assertEquals(user.getAge(), 100);
}
2. 영속성 컨텍스트와 JPQL
- JPQL로 엔티티 조회 시, 영속성 컨텍스트에서 관리 ✅ (임베디드, 스칼라 타입 관리 ❌)
JPQL 조회 흐름
- DB 조회
- JPQL은 즉시 SQL로 변환됨
- 영속성 컨텍스트 확인
- 영속성 컨텍스트는 영속 상태인 엔티티의 동일성을 보장함
- -> DB에서 조회한 엔티티가 존재 O, 영속성 컨텍스트 값을 반환
- -> DB에서 조회한 엔티티가 존재 X, 영속성 컨텍스트에 조회 값 저장 및 반환
entityManager.find() 조회 흐름
- 영속성 컨텍스트 확인
- DB 조회
예제) JPQL 엔티티 조회
더보기
@Test
@Transactional
void jpql_persistence_context_flow() {
User user = new User("test");
user.setAge(20);
entityManager.persist(user);
entityManager.flush();
String updateJpql = "UPDATE User u SET u.age = 100";
entityManager.createQuery(updateJpql).executeUpdate();
User result = entityManager.createQuery("SELECT u FROM User u WHERE u.name = 'test'", User.class)
.getSingleResult();
assertEquals(result.getAge(), 20);
}
예제) entityManager.find()
더보기
@Test
@Transactional
void entityManager_persistence_context_flow() {
User user = new User("test");
user.setAge(20);
entityManager.persist(user);
entityManager.flush();
entityManager.clear(); // 1차 캐시 초기화
// 1차 캐시에 없음 -> db 조회
User result1 = entityManager.find(User.class, user.getId());
User result2 = entityManager.find(User.class, user.getId());
assertSame(result1, result2);
}
3. JPQL과 플러시 모드
FlushMode.AUTO
- JPQL 실행 전에 자동적으로 flush가 호출됨
- 영속성 컨텍스트와 데이터베이스간의 불일치를 완전히 차단
- 너무 많은 플러시로 인해 성능상 손해 발생
예제
더보기
@Test
@Transactional
void flushmode_auto() {
entityManager.setFlushMode(FlushModeType.AUTO);
User user = new User("test");
entityManager.persist(user);
// JPQL 실행 전에 flush 발생 -> 동기화
User result = entityManager.createQuery("SELECT u FROM User u WHERE u.name = 'test'", User.class).getSingleResult();
assertEquals(result.getName(), user.getName());
}
FlushMode.COMMIT
- 커밋 시에만 플러시하도록 모드를 설정 (자동 flush를 해제)
'Spring > Spring Data JPA' 카테고리의 다른 글
[자바 ORM 표준 JPA 프로그래밍] 13. 웹 애플리케이션과 영속성 관리 (1) | 2025.04.26 |
---|---|
[자바 ORM 표준 JPA 프로그래밍] 12. 스프링 데이터 JPA (0) | 2025.04.25 |
[자바 ORM 표준 JPA 프로그래밍] 10-4. 객체지향 쿼리 언어: Native SQL (0) | 2025.04.24 |
[자바 ORM 표준 JPA 프로그래밍] 10-2. 객체지향 쿼리 언어: Criteria (0) | 2025.04.24 |
[자바 ORM 표준 JPA 프로그래밍] 10-1. 객체지향 쿼리 언어: JPQL (0) | 2025.04.24 |