[JPA] @Query, @Modifying, @Transactional 에 대하여

Jun·2023년 2월 20일
0

JPA

목록 보기
1/2
post-thumbnail

📙 JPA에서의 UPDATE문

Spring Data JPA를 적용하게 되면 SELECT 문은 JPA를 사용해 쿼리를 만들 수 있지만 INSERT, UPDATE, DELETE 와 같은 DML을 특정 조건을 곁들여 사용할 경우 쿼리를 직접 작성해야 한다.

이 때 쿼리는 JPQL 로 작성하게 된다.

📖 JPQL이 무엇인가 ?

JPQL이란 Java Persistence Query Language 의 줄임말이며 Query를 테이블 기준이 아닌 엔티티 기준으로 작성된 쿼리문을 의미한다.

예를 들어 Car 라는 Entity가 있고 number, kind 라는 field가 있다고 가정해보자.
하지만 이 Entity에 해당하는 DB 테이블 이름은 test이다.

아래의 Entity와 쿼리들은 아래에도 사용되니 참고바랍니다.

@Entity
@Table(name="test")
public class Car {
	@Id
	public String number;
    public String kind;
}

만약 number 가 1234 이고, kind 가 bus 가 저장되어 있고, bus인 차의 번호를 1111 로 변경하고자 한다면 아래와 같은 쿼리가 작성되어야 한다.

UPDATE test SET number = "1111" WHERE kind="bus";

하지만 JPQL은 객체 지향 쿼리이기 때문에 Entity로 지정된 Car를 사용해 아래와 쿼리를 작성해야 한다.

UPDATE Car car SET car.number="1111" WHERE car.kind="bus";

📖 @Query

Spring 공식 문서 : '리포지토리 메서드에서 직접 파인더 쿼리를 선언하는 주석입니다.'

@Query Annotation은 JPA를 사용하여 UPDATE 문, DELETER 문과 같은 DML을 수행하기 위해 쿼리를 직접 작성해야 할 때 사용한다.

위에서 잠깐 언급했듯이 쿼리를 작성할 때 JPQL로 작성해야한다.

JPQL을 설명하며 작성했던 쿼리를 Parameter를 전달받고 @Query Annotation 안에 작성할 경우 아래와 같이 작성하게 된다.

@Repository
public inferface CarRepository extends JpaRepository<Car, Long> {
	@Query("UPDATE Car car SET car.number = :number WHERE car.kind = :kind")
	int updateCar(@Param("number") String number, @Param("kind") String kind);
}

하지만 위와 같이 작성하여 실행하게 되면 오류가 발생하게 된다.
왜냐하면 @Query Annotation으로 쿼리를 작성할 경우 또 다른 것이 필요하기 때문이다.

📖 @Modifying

Spring 공식 문서 : '쿼리 메서드가 실행해야 하는 방식을 변경하므로 쿼리를 수정하는 것으로 간주되어야 함을 나타냅니다. 이 주석은 쿼리 주석을 통해 정의된 쿼리 메서드에 사용되는 경우에만 고려됩니다. 기본 데이터 액세스 API를 이미 제어하거나 이름으로 수정하는지 여부를 지정하므로 메서드 이름에서 파생된 사용자 지정 구현 메서드 또는 쿼리에는 적용되지 않습니다.'

사실 완벽하게 이해하지 못 했다. 내가 대략적으로 이해한 내용은 아래와 같다.

@Modifying은 @Query로 INSERT, UPDATE, DELETE 및 DDL을 직접 작성하여 사용할 때 수정하여 직접 작성한 쿼리라는것을 명시한다. JPA를 통해 만들어진 메서드 쿼리에는 필요없다.

이정도가 아닐 까 싶다.

이 Modifying Annotation에는 2가지 설정을 지정할 수가 있다.

  • clearAutomatically : modifying query를 실행한 직후에 영속성 컨텍스트를 clear 시킨다.
  • flushAutomatically : modifying query를 실행하기 직전에 영속성 컨텍스트를 flush 한다.

위 2가지 설정 모두 default 값을 false 이다.

@modifying으로 명시된 Query를 실행할 시 바로 DB에 접근하기 때문에 default 값으로 지정하였을 시 영속성 컨텍스트에 존재하는 모든 entity가 DB에 commit 되지 않는 다는 이야기다.

이 entity와 영속성 컨텍스트에 대한 내용은 다음 포스팅에서 다루도록 하겠다.
설정을 적용하지 않는 다면 DB와 영속성 컨텍스트의 정합성이 맞지 않는 다는 것만 알아두자.

@Modifying Annotation 까지 추가한 updateCar method이다.

이것까지 적용한 후 실행을 하게 되면 Executing an update/delete query 라는 에러가 발생하게 된다.

@Repository
public inferface CarRepository extends JpaRepository<Car, Long> {
	@Modifying(clearAutomatically = true)
	@Query("UPDATE Car car SET car.number = :number WHERE car.kind = :kind")
	int updateCar(@Param("number") String number, @Param("kind") String kind);
}

📖 @Transactional

JPA를 사용하여 UPDATE, DELETE 문을 위에서 했던 방식으로 직접 작성하여 실행할 때는 Transaction 처리가 필요하다. 그것을 도와주는 Annotation 이라고 생각하면 된다.

해당 쿼리를 실행하던 도중 에러가 발생하게 되면 Rollback 을 진행한다.
@Transactional까지 추가한 최종 코드는 아래와 같다.

@Repository
public inferface CarRepository extends JpaRepository<Car, Long> {
	@Transactional
	@Modifying(clearAutomatically = true)
	@Query("UPDATE Car car SET car.number = :number WHERE car.kind = :kind")
	int updateCar(@Param("number") String number, @Param("kind") String kind);
}

📙Reference
1. @Query Spring 공식 문서
2. @Modifying Spring 공식 문서
3. @Modifying 관련 블로그

profile
I'll be a Back-End Developer

0개의 댓글

관련 채용 정보