[JPA] 벌크 연산(Bulk Operation)이란 무엇일까?
🎯 벌크 연산(Bulk Operation)이란?
JPA 에서 벌크 연산은 대량의 데이터에 대해 한번에 변경(삽입, 수정, 삭제)을 수행하는 방법 입니다. 일반적으로 벌크 연산은 많은 양의 데이터를 한 번에 처리해야 할 때 사용합니다. JPA 에서는 JPQL 또는 Criteria API 를 사용해 벌크 연산을 수행하며, 벌크 연산은 성능을 크게 향상 시킬 수 있습니다. 하지만 벌크 연산은 영속성 컨텍스트 (Persistence Context) 를 무시하고 직접 DB 에 접근해 쿼리를 실행하기 때문에 주의할점이 있습니다.
벌크 연산에 특징에 대해 정리하면 다음과 같습니다.
✅ 영송석 컨텍스트 무시 : 벌크 연산은 영속성 컨텍스트에 있는 엔티티가 아닌 DB 에 직접 수행되므로, 벌크 연산 후에 영속성 컨텍스트와 DB 간의 데이터 가 일치하지 않을 수 있기에 벌크 연산 후 영속성 컨텍스트 초기화 or 동기화 작업이 필요합니다.
✅ 성능 최적화 : 한번의 SQL 쿼리르 여러 행을 처리하기에, 다수의 엔티티를 반복적으로 처리하는 것보다 성능이 좋습니다.
✅ 쓰기 작업에 적합 : 데이터를 수정하거나 삭제할 때 주로 사용되며, 읽기 작업에는 자주 사용되지 않습니다.
🎯 예제 코드
✅ Member
@Entity
public class Member {
@Id @GeneratedValue
private Long id;
private String name;
private int age;
}
✅ MemberService
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class MemberService {
private final MemberRepository memberRepository;
public MemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
@Transactional
public int updateMemberAgeInBulk() {
// 벌크 업데이트 실행
return memberRepository.bulkUpdateMemberAge();
}
@Transactional
public int deleteOldMembersInBulk() {
// 벌크 삭제 실행
return memberRepository.bulkDeleteMembers();
}
}
✅ MemberRepository
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
@Repository
public interface MemberRepository extends CrudRepository<Member, Long> {
// JPQL을 사용한 벌크 업데이트
@Modifying
@Transactional
@Query("UPDATE Member m SET m.age = 18 WHERE m.age < 18")
int bulkUpdateMemberAge();
// JPQL을 사용한 벌크 삭제
@Modifying
@Transactional
@Query("DELETE FROM Member m WHERE m.age >= 60")
int bulkDeleteMembers();
}
벌크 처리를 할때 @Modifying, @Query, @Transactional 3개의 애노테이션을 함께 사용합니다.
하지만 @Modifying 을 사용할때 주의할 점이 있는데 바로 영속성 컨텍스트와 DB 의 데이터가 달라질 수 있는 상황을 주의 해야 합니다.
JPA 에선 처음 조회되는 데이터는 영속성 컨텍스트에 1차 캐시로 저장되어 DB 접근 횟수를 줄입니다. 하지만 @Modifying 과 @Query 를 사용한 벌크 연산을 진행하면 영속성 컨텍스트 에 있는 정보와 DB 에 있는 정보가 달라 집니다.
따라서 @Modifying 속성중 clearAutomatically 을 true 로 바꿔줘 자동으로 영속성 컨텍스트를 초기화 해야 합니다. 수정 한 코드는 다음과 같습니다.
💡 clearAutomatically 란 ?
clearAutomatically = true
이 속성은 벌크 연산 후 자동으로 영속성 컨텍스트를 비워 데이터베이스와의 일관성을 유지하도록 설정합니다.
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
@Repository
public interface MemberRepository extends CrudRepository<Member, Long> {
// JPQL을 사용한 벌크 업데이트
@Modifying(clearAutomatically = true)
@Transactional
@Query("UPDATE Member m SET m.age = 18 WHERE m.age < 18")
int bulkUpdateMemberAge();
// JPQL을 사용한 벌크 삭제
@Modifying(clearAutomatically = true)
@Transactional
@Query("DELETE FROM Member m WHERE m.age >= 60")
int bulkDeleteMembers();
}