[삽질 피하기] @Query, @Modifying, @Transactional 어노테이션 사용법

2021. 4. 21. 21:21삽질 피하기

1. @Query

Spring JPA를 다루다보면 쿼리를 직접 정의하여 사용할 경우가 존재한다. 이 때 @Query 어노테이션을 이용하여 정의가 가능하다. 단, @Query 어노테이션에 정의될 필드명은 테이블이 아닌 Entity 클래스와 Entity 속성 이름이다. 파리미터의 값은 @Param 어노테이션을 통해 받아올 수 있다.

 

예를 들어, id가 일치하고 특정 기간('WHERE') 내에 있는 각 날짜별('GROUP BY') 데이터의 평균('AVG')을, 반환하고자 하는 객체에 매핑하여 가져오는 방법은 다음과 같다.

@Query("SELECT new com.example.dto.ResponseDto(" +
        "AVG(test.age), AVG(test.value), test.regDate) " +
        "FROM TEST test " +
        "WHERE test.id = :id " +
        "AND test.regDate >= :startDate AND test.regDate <= :endDate " +
        "AND test.name IS NOT NULL AND test.status IS NOT NULL " +
        "GROUP BY test.regDate")
List<ResponseDto> findAvgByIdAndRegDate(
        @Param("id") Long id,
        @Param("startDate") LocalDate startDate,
        @Param("endDate") LocalDate endDate);

※ 여러 줄로 쿼리를 나누어서 정의할 경우, 각 줄의 끝에는 반드시 공백이 존재해야 쿼리문이 에러 없이 동작한다.

 

 

 

2. @Modifying

이 어노테이션은 @Query 어노테이션으로 작성된 조회를 제외한 데이터에 변경이 일어나는 삽입('INSERT'), 수정('UPDATE'), 삭제('DELETE') 쿼리 메서드를 사용할 때 필요하다. 주로 복잡한 벌크 연산 - 다중 UPDATE, DELETE를 연산을 하나의 쿼리로 수행할 때 사용한다.

 

clearAutomatically 속성의 경우, @Modifying 이 붙은 해당 쿼리 메서드 실행 직후, 영속성 컨텍스트를 clear 할 것인지를 지정할 수 있다. default는 false이나, 영속성 컨텍스트의 1차 캐시와 관련하여 문제가 발생할 수 있다.

 

JPA에서는 영속성 컨텍스트에 있는 1차 캐시를 통해 엔티티를 캐싱하고 DB의 접근 횟수를 줄여 성능을 개선한다. 1차 캐시는 @Id값을 Key 값으로 엔티티를 관리한다. 따라서 findById 메서드를 통해 엔티티를 조회할 경우 @Id가 1차 캐시에서 존재한다면 DB에 접근하지 않고 캐싱된 엔티티를 반환한다.

 

하지만 벌크 연산을 통해 데이터 변경 쿼리를 실행하고 해당 엔티티를 조회하면 1차 캐시를 포함한 영속성 컨텍스트를 무시하고 바로 쿼리를 실행하기 때문에 영속성 컨텍스트는 데이터 변경을 알 수 없게 된다. 즉, 벌크 연산을 수행하게 되면 1차 캐시인 영속성 컨텍스트의 값과 DB의 값이 차이가 생긴다. 하지만 clearAutomatically 속성을 이용한다면 이와 같은 데이터 동기화 문제를 해결할 수 있다.

 

결국, @Modifying에 clearAutomatically 속성을 사용하지 않는다면, 1차 캐시인 영속성 컨텍스트의 값과 DB에 적용된 값이 다를 수 있기 때문에 데이터 동기화를 할 수 있도록 활성화해주어야 한다.

 

 

 

 

3. @Transactional

일반적으로 @Transactional 어노테이션은 클래스 전체에 씌우는 경우가 대부분이지만, 'readOnly' 속성을 이용하지 않을 경우 모든 메서드마다 각각 적용해주어야 한다.

 

@DataJpaTest 어노테이션에 기본적으로 포함되어 있기에 테스트 메서드를 실행 시 데이터를 RollBack해준다. 만약, 테스트를 수행하고 DB에서 동작 여부를 확인하고 싶다면 @RollBack(false)를 이용하여 확인할 수 있다.


[참고] devhyogeon.tistory.com/4

728x90