@Modifying
은 JPA에서 데이터를 변경하는 쿼리(INSERT, UPDATE, DELETE)를 실행할 때 사용하는 어노테이션입니다.
@Query
어노테이션과 함께 사용!
@Repository
public interface MemberRepository extends JpaRepository<Member, Long> {
@Modifying
@Query("UPDATE Member m SET m.active = :active WHERE m.email = :email")
int updateMemberActive(@Param("email") String email,
@Param("active") MemberActive active);
}
@Modifying(clearAutomatically = true) // 영속성 컨텍스트 자동 초기화
@Query("UPDATE Member m SET m.nickName = :nickName WHERE m.email = :email")
int updateMemberNickName(@Param("email") String email,
@Param("nickName") String nickName);
@Service
@Transactional
public class MemberService {
private final MemberRepository memberRepository;
public void updateMember(String email, String nickName) {
// @Modifying이 있는 쿼리는 반드시 @Transactional 내에서 실행되어야 함
memberRepository.updateMemberNickName(email, nickName);
}
}
벌크연산
이란?
: 여러 건(대량의 데이터)을 한 번에 수정하거나 삭제하는 방법
예시
public interface ProductRepository extends JpaRepository<Product, Long> {
@Modifying
@Query("UPDATE Product p SET p.price = :newPrice WHERE p.id IN :productIds")
int updateProductPricesInBatch(List<Long> productIds, double newPrice);
}
String sql =
"update Member m" +
"set m.salary = m.salary * 1.1" +
"where m.salary < :salary";
int resultCount = em.createQuery(sql)
.setParameter("salary", 30000000)
.executeUpdate();
em.clear(); // 벌크 연산 수행 후 영속성 컨텍스트 초기화
벌크 연산을 사용할 경우 주의사항 -> JPA에서 하는 게 아니라 Native Query를 사용,
❗ 벌크 연산은 영속성 컨텍스트를 무시하고 DB에 직접 쿼리 한다는 점을 주의해야 한다
=> 그래서 DB 정합성
이 깨질 수 있다.
벌크 연산 수행 후 영속성 컨텍스트 초기화
@Modifying도 벌크연산으로 사용할 수 있습니다.
// 벌크 연산
@Modifying
@Query("UPDATE Member m SET m.active = 'N' WHERE m.lastLoginDate < :date")
int deactivateInactiveMembers(@Param("date") LocalDateTime date);
벌크 연산 주의
대량 데이터 수정 시 영속성 컨텍스트 관리 중요
clearAutomatically = true 권장
JPA에서는 1차 캐시라는 기능이 있다. 1차 캐시를 간단하게 설명하자면 영속성 컨텍스트에 있는 1차 캐시를 통해 엔티티를 캐싱하고, DB 접근 횟수를 줄임으로써 성능을 개선한다.
그런데 @Modifying과 @Query를 사용한 벌크 연산에서 1차 캐시와 관련하여 문제가 발생한다.
JPA에서 조회를 실행할 때 1차 캐시를 확인해서 해당 엔티티가 1차 캐시에 존재한다면 DB에 접근하지 않고, 1차 캐시에 있는 엔티티를 반환한다. 하지만 벌크 연산은 1차 캐시를 포함한 영속성 컨텍스트를 무시하고 바로 Query를 실행하기 때문에 영속성 컨텍스트는 데이터 변경을 알 수가 없다.
즉 벌크 연산 실행 시 1차 캐시(영속성 컨텍스트)와 DB의 데이터 싱크가 맞지 않게 된다.
만약 벌크 연산을 실행하고 다시 해당 데이터를 조회하면 영속성 컨텍스트에 과거 값이 남아 문제가 발생한다.
이 경우 영속성 컨텍스트를 비워주는 작업이 필요하다.
@Modifying의 clearAutomatically=true 속성을 사용해 변경 후 자동으로 영속성 컨텍스트를 초기화할 수 있다.
해당 속성을 추가하게 되면, 조회를 실행할 때 1차 캐시에 해당 엔티티가 존재하지 않기 때문에 DB 조회 쿼리를 실행하게 된다. (데이터 동기화 문제 해결)
@Modifying(clearAutomatically = true)
@Modifying 어노테이션은 기본적으로 @Transactional과 함께 사용된다. 변경 작업은 트랜잭션 내에서 실행되어야 한다. 완료되지 않은 변경 작업이 여러 작업에 영향을 줄 수 있기 때문이다. 이를 통해 데이터베이서에 대한 변경 작업을 수행할 때 원자성, 일관성, 독립성, 지속성을 보장할 수 있게 된다.