Backend/JPA

@Modifying 알아보기

컴공오지마라 2025. 2. 3. 10:00

1. 서론

SpringDataJpa를 사용할때 JPQL을 @Query 와 함께 사용한다. @Query 어노테이션은 기본적으로 읽기 전용으로 간주되는데, UPDATE, DELETE 쿼리를 작성할 경우 @Modifying 어노테이션을 함께 붙여줘야한다. 이번 포스트에서는 @Modifying 에 대해서 간단히 알아보고자 한다.

2. 적용하며 이해하기

@Query("delete from Member m where m.id =:id")
void deleteById(Long id);

 

위 코드에 @Modifying을 붙이지 않고 실행시 아래와 같은 에러가 발생한다

 

org.springframework.dao.InvalidDataAccessApiUsageException: Query executed via 'getResultList()' or 'getSingleResult()' must be a 'select' query [delete from Member m where m.id =:id]
org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:368)
org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:246)

  ... 중략

 

 

에러의 핵심 로그는 아래와 같다 .

'getResultList()' or 'getSingleResult()' must be a 'select' query [delete from Member m where m.id =:id]

 

내부 동작 원리를 살펴보자.

  • @Query로 작성된 JPQL 쿼리는 기본적으로 조회 쿼리로 간주된다.
  • 따라서 JPA는 결과를 조회하기 위해 getResultList()나 getSingleResult() 를 사용하려고 시도한다.
  • 그러나 쿼리가 DELETE 문이므로, 조회 메서드로 결과를 반환할 수 없으므로 에러를 던진다.
따라서 UPDATE, DELETE와 같은 데이터 변경 JPQL에는 @Modifying를 반드시 함께 사용하자!

 

@Modifying
@Query("delete from Member m where m.id =:id")
void deleteById(Long id);

3.@Modifying 속성 알아보기

SpringDataJpa는 기본으로 제공하는 쿼리 메소드 및 메소드 이름으로 쿼리를 생성하는 기능을 제공한다. 그럼에도 JPQL을 사용하는 대표적인 이유로 복잡한 조회 조건 혹은 벌크 쿼리 사용이 있다. 즉, @Modifying은 벌크 연산(DELETE, UPDATE)과 주로 사용되며 이를 위해 영속성 컨텍스트와 관련된 두가지 속성을 제공한다.

 

Modifying 어노테이션

public @interface Modifying {

	boolean flushAutomatically() default false;
	boolean clearAutomatically() default false;
}

 

flushAutomatically

  • 연산이 실행되기 전에, JPA가 자동으로 플러시를 수행할지 여부를 결정한다.
  • true로 설정하면, 연산 실행 전에 영속성 컨텍스트의 변경 내용을 DB에 반영한다.

즉, 쿼리가 실행되기 전에 영속성 컨텍스트의 변경 사항을 DB에 반영해야 일관성을 유지하는 경우에 사용하자.

 

clearAutomatically

  • 연산이 수행된 후, 영속성 컨텍스트를 자동으로 초기화할지 여부를 결정한다.
  • true로 설정하면, 연산 후 영속성 컨텍스트가 비워진다.

JPQL로 작성된 DELETE, UPDATE 쿼리는 영속성 컨텍스트를 무시하고 직접 데이터베이스를 변경하므로 영속성 컨텍스트와 데이터베이스 상태 간에 불일치가 발생할 수 있다. 즉 이러한 쿼리(주로 벌크 연산)를 수행 후, 컨텍스트를 초기화해 이러한 불일치를 방지할 수 있다.