Spring/JPA

[JPA] 벌크 연산(Bulk Operation)이란 무엇일까?

신민석 2024. 9. 7. 05:12

🎯 벌크 연산(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();
}