JPA를 사용하다 보면 대부분은 엔티티 객체를 통해 데이터를 다루지만, 가끔 직접적인 쿼리를 작성해야 할 때가 있습니다. 특히, 데이터를 삽입, 수정, 삭제하는 쿼리는 엔티티만으로 처리하기 어려운 경우가 발생할 수 있습니다.
이럴 때 Spring Data JPA에서 제공하는 @Modifying 어노테이션을 사용하면, 데이터를 수정하거나 삭제하는 쿼리를 안전하고 간편하게 실행할 수 있습니다. 이번 글에서는 @Modifying 애너테이션의 개념과 활용 방법에 대해 알아보겠습니다.
@Modifying 어노테이션이란❓
@Modifying 어노테이션은 Spring Data JPA에서 데이터베이스 조작(수정, 삭제, 삽입) 쿼리를 실행할 때 사용하는 어노테이션입니다.
@Modifying 어노테이션의 역할
@Modifying 어노테이션은 데이터베이스에 직접 영향을 주는 쿼리를 작성할 때 사용됩니다.
Spring Data JPA의 @Query 어노테이션과 함께 사용되며, UPDATE, DELETE, INSERT와 같은 쿼리를 처리할 때 @Modifying을 추가하지 않으면 예외가 발생합니다.
@Modifying
@Query("UPDATE Hub h SET h.is_deleted = true WHERE h.id = :hubId")
int markAsDeleted(@Param("hubId") UUID hubId);
위 코드처럼 UPDATE 쿼리를 사용할 때, @Modifying 어노테이션을 명시적으로 붙여줘야 해당 메서드가 데이터를 수정하는 작업임을 Spring이 인지하고 올바르게 처리할 수 있습니다.
@Modifying 어노테이션의 동작 방식
@Modifying 어노테이션은 JPA의 엔티티 매니저(EntityManager)를 통해 직접적으로 데이터베이스의 데이터를 변경합니다.
일반적인 조회 쿼리(SELECT)와 달리 데이터베이스의 데이터를 수정하거나 삭제하는 작업에서는 트랜잭션이 필요하며, 이를 위해 별도의 처리가 요구됩니다.
Spring Data JPA는 @Modifying 어노테이션이 붙은 메서드를 호출할 때 자동으로 트랜잭션을 시작하고, 메서드가 종료되면 해당 트랜잭션을 커밋 또는 롤백합니다.
@Transactional
@Modifying
@Query("UPDATE Product p SET p.price = :newPrice WHERE p.id = :productId")
int updateProductPrice(@Param("productId") Long productId, @Param("newPrice") BigDecimal newPrice);
만약 @Transactional 어노테이션이 붙어 있지 않으면 데이터 변경 사항이 적용되지 않으므로, 대부분의 경우 @Transactional과 함께 사용하는 것이 권장됩니다.
위 예시처럼 @Modifying과 @Transactional을 함께 사용하여 Product 엔티티의 가격을 업데이트합니다. @Transactional이 없으면 데이터베이스 수정이 적용되지 않습니다.
@Modifying 어노테이션 사용 시 주의사항
트랜잭션 필수
- @Modifying 메서드는 데이터 변경을 동반하므로 트랜잭션 처리가 필수적입니다.
- 만약 @Transactional이 누락되면 쿼리가 실행되어도 데이터베이스에
변경 사항이 반영되지 않습니다.
벌크 연산 주의
- UPDATE, DELETE와 같은 벌크 연산은 영속성 컨텍스트를 무시하고 바로 데이터베이스에 적용됩니다.
- 즉, 영속성 컨텍스트에 남아 있는 엔티티와 불일치가 발생할 수 있습니다. 따라서 벌크 연산 이후에는 영속성 컨텍스트를 clear()하거나, 벌크 연산 전에 필요한 데이터를 미리 처리하는 것이 좋습니다.
벌크 연산이란❓
벌크 연산은 데이터베이스에서 한 번의 쿼리로 여러 개의 레코드를 동시에 수정, 삭제하는 작업을 의미합니다. 일반적으로 UPDATE, DELETE, INSERT와 같은 SQL 명령어를 한 번에 여러 행에 적용하는 경우를 말합니다.
JPA에서는 벌크 연산을 통해 다수의 엔티티를 일괄 처리할 수 있으며, 이를 통해 성능을 개선할 수 있습니다. 예를 들어, 수천 개의 레코드를 하나씩 처리하는 대신, 한 번의 쿼리로 한꺼번에 업데이트하거나 삭제하면 데이터베이스와의 통신 횟수를 줄일 수 있습니다.
@Modifying
@Query("UPDATE Post p SET u.views = p.views + 1")
int incrementViews();
JPA에서는 벌크 연산을 @Modifying 어노테이션과 함께 @Query 어노테이션을 사용하여 구현합니다.
위 의 예에서는 JPA가 데이터베이스에서 특정 게시물의 조회수를 1 증가시키는 쿼리를 실행하며, 변경된 행의 개수를 반환합니다.
벌크 연산의 장점
성능 최적화
- 게시물마다 하나씩 조회수 업데이트 쿼리를 실행하는 대신, 한 번의 벌크 연산으로 성능이 크게 향상됩니다.
통신 비용 감소
- 데이터베이스와의 통신을 한 번으로 줄여서, 여러 번의 통신에 소모되는 비용을 절약할 수 있습니다.
'Framework > JPA' 카테고리의 다른 글
[QueryDSL] QueryDSL - @QueryProjction 어노테이션이란❓ (0) | 2024.10.06 |
---|---|
[JPA] 프록시(proxy)객체란 무엇일까❓ (1) | 2024.09.15 |
[JPA] Spring Data JPA의 페이징과 정렬을 쉽게 구현하는 방법: Pageable과 PageRequest (0) | 2024.09.02 |
[JPA] JPA에서 단방향 및 양방향 관계 이해 (@ManyToOne, @OneToMany, @OneToOne, @ManyToMany) (1) | 2024.08.23 |
[JPA] JPA에서 낙관적 락(Optimistic Locking)을 통한 동시성 제어하기 (@Version) (0) | 2024.08.22 |