티스토리 뷰
[QueryDSL] Projections, @QueryProjection 을 사용하여 DTO로 반환하기
종벌🍀 2023. 11. 13. 17:23
엔티티가 연관관계없이 특정 컬럼들만 조인해서 뽑아와야 할때가 있다.
이때 엔티티에 값을 담아서 리턴할 수 없어서
DTO에 반환해야 하는데
Projections 를 이용하면 보기좋게 코드를 작성할 수 있다.
먼저 Projections 내의 3가지 bean, fields, constructor 를 이용하는 방법과
@QueryProjection 어노테이션을 이용하는 방법이 있다.
활용해본 결과
Projections의 생성자를 이용하는 방법과
@QueryProjection 어노테이션을 이용하는 방법이 좋은 것 같고
프로젝트 상황에 따라 이 2가지를 사용하는게 이점이 있다고 본다.
bean과 fields 방식은 DTO의 필드들과 네이밍이 동일하지 않으면 삽입이 안되서
다른 네이밍들은 직접 다 잡아줘야 한다.
AEntity 와 BEntity 는 아래와 같고 둘은 연관관계가 없다.
ABDTO 에 담아 리턴해보고자 한다.
@Entity
@Table(name = "Atable")
@Getter
@NoArgsConstructor
public class AEntity {
@Id
private int id;
private String tableName;
private String regDate;
private String memberId;
}
@Entity
@Table(name = "Btable")
@Getter
@NoArgsConstructor
public class BEntity {
@Id
private String memberId;
private String branchNm;
private String orgNm;
private String name;
}
@Data
@NoArgsConstructor @AllArgsConstructor
public class ABDTO {
private int id;
private String tableName;
private String regDate;
private String memberId;
private String branchName;
private String name;
private String departName;
}
1. Projections.bean
public List<ABDTO> findAll_bean() {
return queryFactory
.select(Projections.bean(ABDTO.class
, aEntity.tableName
, aEntity.memberId
, bEntity.branchNm.as("branchName")
, bEntity.name
, bEntity.orgNm.as("departName")
))
.from(aEntity)
.join(bEntity).on(aEntity.memberId.eq(bEntity.memberId))
.fetch();
}
Projections.bean 방식은 DTO의 setter 를 이용하기 때문에 DTO 필드에 setter가 명시되어야 합니다.
DTO를 영속성으로 사용하고 계시다면 setter에 의해 데이터가 변할 수 있기 때문에 잘 관리해야 합니다.
그리고 DTO의 필드와 네이밍이 다를 경우 set이 되지 않습니다.
as를 사용하여 네이밍을 DTO 필드와 동일하게 맞춰주어야 값이 set 됩니다.
2. Projections.fields
public List<ABDTO> findAll_fields() {
return queryFactory
.select(Projections.fields(ABDTO.class
, aEntity.tableName
, aEntity.memberId
, bEntity.branchNm.as("branchName")
, bEntity.name
, bEntity.orgNm.as("departName")
))
.from(aEntity)
.join(bEntity).on(aEntity.memberId.eq(bEntity.memberId))
.fetch();
}
Projections.fields 방식은 필드에 직접데이터를 넣어줍니다.
그래서 네이밍이 동일하지 않으면 삽입이 되지 않아 as로 필드 네이밍을 맞춰주어야 합니다.
3. Projections.constructor
public List<ABDTO> findAll_constructor() {
return queryFactory
.select(Projections.constructor(ABDTO.class
, aEntity.id
, aEntity.tableName
, aEntity.regDate
, aEntity.memberId
, bEntity.branchNm
, bEntity.name
, bEntity.orgNm
))
.from(aEntity)
.join(bEntity).on(aEntity.memberId.eq(bEntity.memberId))
.fetch();
}
Projection.constructor 는 DTO 생성자 기반으로 삽입됩니다.
그래서 네이밍 상관없이 생성자 필드 순으로 바인딩 할 수 있으며
단, 필드가 많을 경우 사용자 실수가 발생 할 수 있습니다.
4. @QueryProjection
@Data
@NoArgsConstructor @AllArgsConstructor
public class ABDTO {
private int id;
private String tableName;
private String regDate;
private String memberId;
private String branchName;
private String name;
private String departName;
@QueryProjection
public ABDTO(String memberId, String name) {
this.memberId = memberId;
this.name = name;
}
}
@Generated("com.querydsl.codegen.DefaultProjectionSerializer")
public class QABDTO extends ConstructorExpression<ABDTO> {
private static final long serialVersionUID = 516547612L;
public QABDTO(com.querydsl.core.types.Expression<String> memberId, com.querydsl.core.types.Expression<String> name) {
super(ABDTO.class, new Class<?>[]{String.class, String.class}, memberId, name);
}
}
public List<ABDTO> findAll_Qdto() {
return queryFactory
.select(new QABDTO(aEntity.memberId, bEntity.name))
.from(aEntity)
.join(bEntity).on(aEntity.memberId.eq(bEntity.memberId))
.fetch();
}
@QueryProtection 어노테이션을 DTO의 생성자에 선언해주고 빌드를 수행하면
QDTO 클래스가 생성되어 해당 QDTO를 이용하여 더 간편하게 사용할 수 있는 장점이 있다.
컴파일 시점에도 오류를 검출할 수 있는 이점이 있으나
전역 계층에 사용하는 DTO를 QueryDSL 에 의존시켜야 하는 단점이 존재한다.
참고 블로그
'Java > JPA, QueryDSL' 카테고리의 다른 글
[QueryDSL] LocalDateTime 형식 DateTimeExpression 으로 년월(YYYYMM)만 비교하기 (0) | 2024.03.07 |
---|---|
[JPA] DB환경별 다중 schema, catalog 엔티티 적용법 (2) | 2023.11.22 |
[QueryDSL] 다중 Datasource 설정하기 (feat.@Qualifier 사용) (0) | 2023.11.13 |
[JPA] LocalContainerEntityManagerFactoryBean의 setJpaPropertyMap을 설정할 때 사용할 수 있는 key 값 목록 (0) | 2023.10.24 |
[Entity] @PrePersist , @PreUpdate 차이 (0) | 2023.09.18 |