본문 바로가기
JAVA/JPA

[JPA] 벌크(Bulk) 연산

by 개미가되고싶은사람 2024. 12. 2.

벌크 연산

JPA의 영속성 컨텍스트 특징 중 하나인 변경감지(Dirty Checking)에는  치명적인 단점이 있습니다. 예를 들어, 영속성 컨텍스트에 100개의 데이터가 있을 때, 이 100개 데이터의 변경사항이 모두 감지되면 JPA는 각 변경사항에 대해 자동으로 update 쿼리를 생성하여 적용합니다. 이 과정에서 각 변경 건마다 update 쿼리가 생성되기 때문에 성능 문제가 발생할 수 있습니다. 이를 해결하기 위해 JPA는 대량의 데이터를 한 번에 UPDATE 또는 DELETE할 수 있는 벌크 연산 기능을 제공합니다.

 

벌크 연산 사용 시 주의사항

벌크 연산은 영속성 컨텍스트를 무시하고 데이터베이스에 직접 쿼리를 전달하는 방식이기 때문에 주의가 필요합니다. 벌크 연산을 실행한 후에 변경된 데이터를 가져오면 영속성 컨텍스트에는 변경되기 전의 데이터가 남아 있게 되어 데이터 일관성이 깨질 수 있습니다. 

 

예를 들어, Member 엔티티의 나이를 변경하고 해당 멤버의 나이를 출력하는 로직을 살펴보겠습니다.

Member member = new Member();
member.setUsername("회원1");
member.setTeam(team1);
em.persist(member);

String query = " update Member m set m.age = 10";
int resultCount = em.createQuery(query)
	.executeUpdate();

Member findMember = em.find(Member.class, member.getId());
System.out.println("findMember = " + findMember.getAge());

findMember에 age가 0이 나온 이유는 앞서 설명한 대로 벌크 연산이 영속성 컨텍스트를 사용하지 않고 데이터베이스에 직접 쿼리를 전달하기 때문입니다. 회원1을 저장할 때, age는 0으로 영속성 컨텍스트에 저장됩니다.
따라서 em.find()는 아직 데이터베이스에 반영이 안된 영속성 컨텍스트에 남아있는 회원1의 데이터를 조회하게 됩니다. 이로 인해 findMember의 age는 0으로 나오게 됩니다.

 

그럼 이런 문제를 어떻게 해결할까요?

 

1. 어떤 로직을 수행할 때 벌크 연산을 먼저 실행

로직을 수행하기 전에 벌크 연산을 먼저 실행하면 데이터베이스의 데이터를 직접 수정할 수 있습니다. 즉, 영속성 컨텍스트를 사용하지 않고 데이터베이스에 데이터가 바로 변경됩니다. 이후 필요한 데이터를 영속성 컨텍스트에 등록하거나 조회할 때 벌크 연산으로 데이터를 수정한 데이터를 사용할 수 있습니다.

 

2. 벌크 연산 수행 후 영속성 컨텍스트 초기화

벌크 연산을 실행 후에는 영속성 컨텍스트를 초기화하여 데이터베이스의 최신 상태를 영속성 컨텍스트에 적용하면 됩니다. 기본적으로 JPA는 쿼리가 발생할 때 자동으로 flush() 메소드를 실행하므로, clear() 메서드를 사용하여 영속성 컨텍스트를 비우면 영속성 컨텍스트에 데이터베이스에 데이터를 가져오면 데이터베이스의 변경 사항이 반영됩니다. 이렇게 함으로써 벌크 연산 후 데이터 일관성이 깨지는 걸 해결할 수 있습니다.

 

참고로 @Modifying 애노테이션 속성 중 clearAutomatically를 true로 설정하면, 쿼리 실행 후 EntityManager가 자동으로 초기화됩니다. - 공식문서

@Modifying( clearAutomatically = true)

 

 

참고

https://velog.io/@hunil99/%EB%B2%8C%ED%81%AC-%EC%97%B0%EC%82%B0%EC%9D%B4-%EB%AD%90%EC%95%BC-%EC%99%9C-%EC%82%AC%EC%9A%A9%ED%95%B4%EC%A3%BC%EC%9D%98%ED%95%A0-%EC%A0%90

 

벌크 연산 (주의할 점)

벌크 연산

velog.io

https://docs.spring.io/spring-data/jpa/reference/jpa/query-methods.html#jpa.modifying-queries

 

JPA Query Methods :: Spring Data JPA

By default, Spring Data JPA uses position-based parameter binding, as described in all the preceding examples. This makes query methods a little error-prone when refactoring regarding the parameter position. To solve this issue, you can use @Param annotati

docs.spring.io