이번 포스팅은 SpringBoot의 Data JPA 벌크연산에 대한 글을 작성하고자 합니다.
1. 벌크 연산
벌크 연산은 여러 건의 데이터를 한 번에 수정하거나 삭제하는 방법으로 대용량 데이터를 한 번에 처리할 때 유용합니다.
현재 주어진 Member table에는 6개의 행이 존재합니다. 만약 25살 이상의 나이에 모두 1살을 더하라는 요청이 오면 , 다음과 같은 쿼리를 생성할 수 있습니다.
update member m
set m.age = m.age + 1
where m.age >= 25;
혹은 Member table에서 member_id가 5 이상인 행을 제거하라는 요청이 오면 쿼리는 다음과 같습니다.
delete from member m
where m.member_id >= 5;
2. 스프링 DATA JPA의 벌크연산
이처럼 한 번에 다량의 데이터를 수정하는 것을 벌크연산이라고 하는데, 스프링 데이터 JPA에서는 벌크연산을 지원합니다.
@Modifying 어노테이션과 함께 벌크 연산 쿼리를 작성하면 해결할 수 있습니다.
@Entity
@Getter
@AllArgsConstructor
@NoArgsConstructor(access = PROTECTED)
public class Member {
@Id @GeneratedValue
@Column(name = "member_id")
private Long id;
private String username;
private int age;
}
@Repository
public interface MemberRepository extends JpaRepository<Member, Long> {
@Modifying(clearAutomatically = true)
@Query("update Member m set m.age = m.age + 1 where m.age >= :age")
int bulkAgePlus(@Param("age") int age);
}
이 글의 목적이기도 한 벌크 연산의 주의점입니다. 벌크 연산은 영속성 컨텍스트를 참조하지 않고 DB에 직접 접근하기 때문에 1차 캐시에 남아있는 캐시와 값이 다를 수 있습니다. 따라서, 만약 영속성 컨텍스트를 초기화하지 않는다면, 변경 이전의 값을 참조할 수 있다는 위험성이 존재합니다.
@Repository
public interface MemberRepository extends JpaRepository<Member, Long> {
@Modifying
@Query("update Member m set m.age = m.age + 1 where m.age >= :age")
int bulkAgePlus(@Param("age") int age);
}
@Modifying(clearAutomatically = true)는 해당 쿼리가 실행 된 후, 영속성 컨텍스트를 초기화하는 역할을 수행합니다. 만약(clearAutomatically = true)가 없다면 벌크 연산 이후에 영속성 컨텍스트에 남아있는 이전의 값이 활용되는 문제점이 발생합니다.