https://lordofkangs.tistory.com/404
JPA는 변경감지(DirtyChecking)로 UPDATE문을 생성하지만 한 번의 변경에 하나의 쿼리만 생성한다. 그래서 여러 건의 수정을 하나의 쿼리로 실행하려면 JPQL을 만들어야 한다. JPQL은 영속성 컨텍스트를 패싱하고 바로 DB에 실행되기에, 영속성 컨텍스트는 변경된 최신 데이터를 갖지 못한다. 그래서 JPQL로 수정쿼리를 실행한 뒤에는 항상 영속성 컨텍스트를 초기화 해야한다.
그럼 스프링데이터JPA에서 여러 건의 데이터를 수정하려면 어떻게 해야할까?
스프링데이터JPA는 개발자가 리포지토리 인터페이스를 명세하면 이에 맞는 리포지토리 구현체를 프록시 객체로 제공한다. 그러므로 개발자는 JPQL이 실행되어야 하는 메소드에 @Query 어노테이션을 달아 사용한다. 그런데 여기서 문제가 있다. @Query 어노테이션은 SELECT문 전용 어노테이션이다. INSERT, DELETE, UPDATE 그리고 DDL은 @Query에 쓰일 수 없다.
public interface MemberRepository extends JpaRepository<Member,Long>{
//SELECT문 사용가능
@Query("SELECT m FROM Member m LEFT JOIN FETCH m.team")
List<Member> findMemberFetchJoin();
//UPDATE문 사용불가능
@Query("UPDATE Member m SET m.age = m.age+1 WHERE m.age >= :age")
int bulkAgePlus(@Param("age") int age);
}
실제로 @Query에 SELECT문이 아닌 다른 쿼리가 들어가면 InvalidJpaQueryMethodException이 발생한다. 이를 해결하기 위해, SpringDataJPA는 @Modifying 어노테이션을 제공한다. @Modifying 어노테이션은 오로지 @Query 어노테이션이 사용된 곳에서만 사용이 가능하다. @Modifying 어노테이션은 SELECT문만 지원하는 @Query 어노테이션을 강화하여, INSERT,UPDATE,DELETE 그리고 DDL 사용이 가능하도록 한다.
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);
}
이로써 파라미터로 입력받은 나이 이상의 회원들의 나이를 +1씩 증가하는 쿼리가 수행 될 수 있다. ( 여러 건의 수정, 벌크연산 )
앞서 JPQL로 벌크연산을 수행하면 영속성컨텍스트가 최신데이터를 가지지 못한다고 하였다. 그래서 영속성컨텍스트를 초기화 해야한다. @Modifying 어노테이션은 수정쿼리 수행 후, 영속성 컨텍스트를 초기화하는 설정도 제공한다.
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);
}
clearAutomatically 속성을 true로 설정하면 영속성 컨텍스트를 초기화를 자동으로 실행한다.
이렇듯, 스프링데이터JPA는 개발자가 원하는 리포지토리를 인터페이스로 명세하면 그에 맞는 리포지토리 구현체를 자동생성하여 제공한다. 그리고 JPQL 같이, 개발자가 직접 관여해야 하는 부분은 어노테이션(주석)으로 처리하도록 지원하고 있다.
참고자료
'JPA > Spring Data JPA' 카테고리의 다른 글
[SpringDataJPA] 사용자정의 인터페이스 (0) | 2023.07.18 |
---|---|
[SpringDataJPA] @EntityGraph (0) | 2023.07.13 |
[SpringDataJPA] 페이징 ( Pageable, Page, Slice ) (0) | 2023.07.12 |
[SpringDataJPA] @Query (0) | 2023.07.11 |
[SpringDataJPA] 메소드 이름으로 쿼리생성하기 (0) | 2023.07.11 |