QueryDSL 사용 시 자동 업데이트 되지 않는 JPA Auditing 수정 날짜 문제 해결 방법
📌 서론
프로젝트를 개발하다가 업데이트 관련 로직은 거의 QueryDSL을 사용하고 있었다. 그러다가 updateDttm 컬럼값이 바뀌지 않는 걸 확인하고 이를 수정하려다 알게 된 사실을 정리해보려고 한다.
Auditing 필드 선언
JPA와 QueryDSL을 사용하여 엔티티의 생성 날짜와 수정 날짜를 자동으로 관리하는 것은 매우 흔한 패턴이다. 다음 코드는 우리가 작성한 생성 날짜와 수정 날짜 클래스다.
/**
* Auditing 필드 - 생성시간 등록
* 이 필드를 상속받으면 생성시간만 auditing이 적용된다.
*/
@ToString
@Getter
@MappedSuperclass
@EntityListeners(value = {AuditingEntityListener.class})
public abstract class CreateDateTimeForEntity {
// 생성시간
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
@CreatedDate
@Column(name = "create_dttm", nullable = false)
private LocalDateTime createDateTime;
}
/**
* Auditing 필드 - 생성시간 + 수정시간 등록
* 이 필드를 상속받으면 생성시간과 수정시간 모두 auditing이 적용된다.
*/
@ToString
@Getter
@MappedSuperclass
@EntityListeners(value = {AuditingEntityListener.class})
public abstract class UpdateDateTimeForEntity extends CreateDateTimeForEntity {
// 수정시간
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
@LastModifiedDate
@Column(name = "update_dttm", nullable = false)
private LocalDateTime updateDateTime;
}
CreateDateTimeForEntity와 UpdateDateTimeForEntity 클래스는 이를 위한 기반 클래스로 구성했다. 이 클래스들은 각각 생성 시간과 수정 시간을 자동으로 추적하도록 @CreatedDate와 @LastModifiedDate 어노테이션을 사용한다.
그러나 QueryDSL을 사용한 업데이트 연산에서 수정 시간이 자동으로 갱신되지 않는 문제가 발생할 수 있다.
이 문제의 원인은 JPA의 생명주기 이벤트와 Spring Data JPA의 Auditing 기능이 엔티티의 상태 변화를 감지하고 반영하는 방식에 있다. JPA는 EntityManager를 통해 엔티티의 생명주기를 관리하며, 엔티티의 상태 변화를 감지하여 데이터베이스와 동기화한다. 반면, QueryDSL을 사용한 업데이트 연산은 EntityManager를 거치지 않고 직접 SQL을 생성하여 실행하기 때문에, JPA의 생명주기 이벤트나 Spring Data JPA의 Auditing 기능이 자동으로 작동하지 않는다.
해결 방법
- 엔티티를 조회하여 수정하기: 엔티티를 먼저 조회한 후 필드 값을 변경하고, 이 변경을 커밋하면 JPA가 엔티티의 상태 변화를 감지하고 @LastModifiedDate 어노테이션이 적용된 필드를 자동으로 업데이트한다.
- QueryDSL에서 제공하는 JPAUpdateClause 사용: QueryDSL이 제공하는 JPAUpdateClause를 사용하면, 엔티티 클래스에 직접 접근하지 않고도 업데이트 쿼리를 생성할 수 있으나, 이 방법 역시 @LastModifiedDate 어노테이션이 자동으로 적용되지 않는다. 따라서, 수정 날짜는 수동으로 관리해야 한다.
- 수동으로 수정 날짜 설정: QueryDSL을 사용해 업데이트 쿼리를 실행하기 전에, 수정 날짜 필드에 현재 시간을 직접 설정해야 한다. 이를 위해 수정 쿼리를 실행하는 코드에서 수정 날짜를 파라미터로 전달하고, 해당 값을 업데이트 쿼리에 포함시킨다.
수동으로 수정 날짜를 설정하는 방법을 보여주는 간단한 예시이다.
// Querydsl 업데이트 쿼리 실행 전 수정 날짜 설정
LocalDateTime now = LocalDateTime.now(); // 현재 시간 계산
QEntity qEntity = QEntity.entity; // 엔티티의 Q타입 인스턴스
long updatedCount = new JPAQueryFactory(entityManager)
.update(qEntity)
.set(qEntity.updateDateTime, now) // 수정 날짜 수동으로 설정
.where(qEntity.id.eq(entityId))
.execute();
이 코드는 JPAQueryFactory를 사용하여 엔티티의 updateDateTime 필드를 현재 시간으로 직접 설정하는 방법을 보여준다. 이렇게 하면 QueryDSL을 사용한 업데이트 연산에서도 수정 날짜를 갱신할 수 있다.
현재 우리도 기존에 있던 update 문에 .set 항목을 추가해 주는 방식으로 해결하고 있다. 방식을 사용하면 QueryDSL을 통해 데이터를 업데이트할 때도 @LastModifiedDate 어노테이션을 사용하지 않고, 수정 날짜를 정확히 제어할 수 있다. 하지만 이 접근법은 Auditing 기능의 자동화 장점을 포기해야 한다는 단점이 있다. 따라서, 애플리케이션의 요구 사항과 상황에 따라 가장 적합한 방법을 선택해야 할 것 같다.
QueryDSL에서 Projections.constructor 사용해서 SQL 함수 사용하기
📣 이 글은 내가 소속된 Team Chillwave에서 진행한 사이드 프로젝트에서 경험한 내용을 정리한 것이다.
다른 팀원인 "개발자의 서랍" 님의 블로그도 방문하면 도움이 될 것 같다 :)
'Spring > Spring Data JPA' 카테고리의 다른 글
QueryDSL 사용할때 Custom Interface와 QueryRepository 단독 빈 등록 방식 비교 (테스트 어노테이션) (0) | 2024.04.01 |
---|---|
JPA에서 Entity 복합키 관리 (@Embeddable, @EmbeddedId) (0) | 2024.04.01 |
JPA와 Spring Data JPA의 차이 (1) | 2024.01.02 |
JPA N+1 문제 해결하기 (fetch join, entityGraph, batch size) (5) | 2023.12.28 |
[Spring Data JPA] ResponseDTO에 기본 생성자 있어야하는 이유 (1) | 2023.11.07 |