티스토리 뷰

반응형

 

 



 

 

 

 

 

엔티티가 연관관계없이 특정 컬럼들만 조인해서 뽑아와야 할때가 있다.

이때 엔티티에 값을 담아서 리턴할 수 없어서

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 에 의존시켜야 하는 단점이 존재한다.

 

 

 

 

 

 



 

 

 

 

 

 

 

 

 

 

 

참고 블로그
 

Querydsl DTO 조회하는 방법(Projection, @QueryProjection)

Projection 연산이란, - 한 Relation의 Attribute들의 부분 집합을 구성하는 연산자입니다. - 결과로 생성되는 Relation은 스키마에 명시된 Attribute들만 가집니다. - 결과 Relation은 기본 키가 아닌 Attribute에

wildeveloperetrain.tistory.com

 

 

반응형
댓글
반응형
최근에 올라온 글
«   2024/09   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
Total
Today
Yesterday