JPA

[ Querydsl ] ํ”„๋กœ์ ์…˜(Projections)๊ณผ ๊ฒฐ๊ณผ๋ฐ˜ํ™˜ (ํ”„๋กœํผํ‹ฐ์ ‘๊ทผ, ํ•„๋“œ์ง์ ‘์ ‘๊ทผ, ์ƒ์„ฑ์ž ์‚ฌ์šฉ)

PYT 2022. 3. 28. 14:00
๋ฐ˜์‘ํ˜•

ํ”„๋กœ์ ์…˜ (Projections) ?

Querydsl์„ ์ด์šฉํ•ด entity์ „์ฒด๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ์กฐํšŒ ๋Œ€์ƒ์„ ์ง€์ •ํ•ด ์›ํ•˜๋Š” ๊ฐ’๋งŒ ์กฐํšŒํ•˜๋Š” ๊ฒƒ์„ ๋งํ•œ๋‹ค.


๊ธฐ๋ณธ

1. ํ”„๋กœ์ ์…˜ ๋Œ€์ƒ์ด ํ•˜๋‚˜์ผ ๋•Œ

List<String> result = queryFactory
            .select(member.username)
            .from(member)
            .fetch();

ํ”„๋กœ์ ์…˜ ๋Œ€์ƒ์ด ํ•˜๋‚˜์ผ๊ฒฝ์šฐ ์œ„์ฒ˜๋Ÿผ ํƒ€์ž…์„ ๋ช…ํ™•ํ•˜๊ฒŒ ์ง€์ •ํ•˜๋ฉด ๋œ๋‹ค.

2. ํ”„๋กœ์ ์…˜ ๋Œ€์ƒ์ด ๋‘๊ฐœ ์ด์ƒ์ผ ๊ฒฝ์šฐ

 List<Tuple> result = queryFactory
            .select(member.username, member.age)
            .from(member)
            .fetch();

   for (Tuple tuple : result) {
      String username = tuple.get(member.username);
      Integer age = tuple.get(member.age);
      System.out.println("username=" + username);
      System.out.println("age=" + age);
}


ํ”„๋กœ์ ์…˜ ๋Œ€์ƒ์ด ๋‘๊ฐœ ์ด์ƒ์ผ ๊ฒฝ์šฐ ๋ช…ํ™•ํ•œ ํƒ€์ž…์„ ์ง€์ •ํ•  ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์— ํŠœํ”Œ์ด๋‚˜ DTO๋กœ ๊ฐ’์„ ์กฐํšŒํ•  ์ˆ˜ ์žˆ๋‹ค.
ํ•˜์ง€๋งŒ ํŠœํ”Œ๋กœ ๊ฐ’์„ ๊ฐ€์ ธ์˜ค๋Š” ๊ฒฝ์šฐ๋Š” Querydsl์— ์ข…์†์ ์ด๊ณ , Model ๊ฐ์ฒด๋ฅผ ๋กœ์ง์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ๊ณผ ๊ฐ™์€ ๋ฌธ์ œ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๊ธฐ์— ์ตœ๋Œ€ํ•œ ์‚ฌ์šฉ์„ ํ”ผํ•˜๊ณ  Repository์—์„œ๋งŒ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค. ๋งŒ์•ฝ Repository ๋ฐ”๊นฅ์œผ๋กœ ๋‚˜๊ฐˆ ๋• DTO๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๊ฒƒ์„ ๊ถŒ์žฅํ•œ๋‹ค.


DTO ์กฐํšŒ

1. ์ˆœ์ˆ˜ JPA์—์„œ DTO ์กฐํšŒ

 

MemberDto

package study.querydsl.dto;
import lombok.Data;
  
  @Data
  public class MemberDto {
      private String username;
      private int age;
      
      public MemberDto() {
      }
      
      public MemberDto(String username, int age) {
          this.username = username;
          this.age = age;
	}
}

 

์ˆœ์ˆ˜ JPA์—์„œ DTO ์กฐํšŒ ์ฝ”๋“œ

List<MemberDto> result = em.createQuery(           
			"select new study.querydsl.dto.MemberDto(m.username, m.age)" +
                  	"from Member m", MemberDto.class)
          			.getResultList();

์ˆœ์ˆ˜ JPA์—์„œ DTO๋ฅผ ์กฐํšŒํ•  ๋•Œ๋Š” new ๋ช…๋ น์–ด๋ฅผ ์‚ฌ์šฉํ•˜๋Š”๋ฐ ์ด ๊ฒฝ์šฐ ์ƒ์„ฑ์ž ๋ฐฉ์‹๋งŒ ์ง€์›ํ•˜๋ฉฐ, DTO์˜ package์ด๋ฆ„์„ ๋‹ค ์ ์–ด์ค˜์•ผํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ฝ”๋“œ๊ฐ€ ์ง€์ €๋ถ„ํ•˜๋‹ค.

 

์ด๋ฅผ ๊ทน๋ณตํ•˜๊ธฐ ์œ„ํ•ด Querydsl๋กœ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜๋ฐ›๋Š” ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•œ๋‹ค.


Querydsl ๋นˆ ์ƒ์„ฑ(Bean population) 

 

๊ฒฐ๊ณผ๋ฅผ DTO๋กœ ๋ฐ˜ํ™˜ํ•  ๋•Œ ์‚ฌ์šฉํ•˜๋ฉฐ,

1) ํ”„๋กœํผํ‹ฐ ์ ‘๊ทผ  2) ํ•„๋“œ ์ง์ ‘ ์ ‘๊ทผ 3) ์ƒ์„ฑ์ž ์‚ฌ์šฉ ์˜ ์„ธ ๊ฐ€์ง€ ๋ฐฉ์‹์ด ์žˆ๋‹ค.

 

 

 1. ํ”„๋กœํผํ‹ฐ ์ ‘๊ทผ

List<MemberDto> result = queryFactory
          .select(Projections.bean(MemberDto.class,
                  member.username,
                  member.age))
          .from(member)
          .fetch();

Projections.bean
setter(bean)๋ฅผ ํ†ตํ•ด ๋ฐ์ดํ„ฐ๋ฅผ ์ธ์ ์…˜ ํ•ด์ฃผ๋ฉฐ ๊ธฐ๋ณธ ์ƒ์„ฑ์ž๊ฐ€ ๋ฌด์กฐ๊ฑด ํ•„์š”ํ•˜๋‹ค.
(Constructor ๋งŒ๋“ค์–ด์ฃผ๊ฑฐ๋‚˜ ๋กฌ๋ณต์“ฐ๋ฉด @NoArgsContructor ์‚ฌ์šฉํ•˜์„ธ์š” !)

 

 2. ํ•„๋“œ ์ง์ ‘ ์ ‘๊ทผ 

List<MemberDto> result = queryFactory
          .select(Projections.fields(MemberDto.class,
                  member.username,
                  member.age))
          .from(member)
          .fetch();

Projections.fields
ํ•„๋“œ์— ๊ฐ’์„ ๋”ฑ ๊ฝ‚์•„์ฃผ๊ธฐ ๋•Œ๋ฌธ์— setter์™€ ๊ธฐ๋ณธ์ƒ์„ฑ์ž๊ฐ€ ํ•„์š”์—†์Šต๋‹ˆ๋‹ค.

 

2-1. ๋ณ„์นญ์ด ๋‹ค๋ฅผ ๋•Œ

package study.querydsl.dto;
import lombok.Data;

@Data
public class UserDto {
    private String name;
    private int age;
}
List<UserDto> fetch = queryFactory
              .select(Projections.fields(UserDto.class,
                member.username.as("name"),
                ExpressionUtils.as(
    			)
             ).from(member)
             .fetch();

ํ”„๋กœํผํ‹ฐ๋‚˜ ํ•„๋“œ ์ ‘๊ทผ ์ƒ์„ฑ ๋ฐฉ์‹์—์„œ ์ด๋ฆ„์ด ๋‹ค๋ฅผ ๋•Œ ์•„๋ž˜์™€ ๊ฐ™์€ ๋ฐฉ๋ฒ•์œผ๋กœ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค.

1) ExpressionUtils.as(sourse, alias) :  ํ•„๋“œ,  ์„œ๋ธŒ ์ฟผ๋ฆฌ์— ๋ณ„์นญ ์ ์šฉ
2) username.as("memberName") : ํ•„๋“œ์— ๋ณ„์นญ ์ ์šฉ

๋Œ€๋ถ€๋ถ„์€ ๊ฐ€๋…์„ฑ๋•Œ๋ฌธ์— as๋ฅผ ์“ฐ๋Š”๋ฐ ์„œ๋ธŒ์ฟผ๋ฆฌ์˜ ๊ฒฝ์šฐ ๋ฌด์กฐ๊ฑด ExpressionUtils.as ๋ฅผ ์จ์•ผํ•œ๋‹ค.

3. ์ƒ์„ฑ์ž ์‚ฌ์šฉ

List<MemberDto> result = queryFactory
          .select(Projections.constructor(MemberDto.class,
                  member.username,
                  member.age))
          .from(member)
          .fetch()
 }

Projections.constructor

๊ฐ’์„ ๋„˜๊ธธ ๋•Œ์ƒ์„ฑ์ž์™€ ์ˆœ์„œ๊ฐ€ ๋งž์•„์•ผ ๋ฐ์ดํ„ฐ๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๋ฉฐ, @AllArgsConstructor ํ•„์š”, setter๊ฐ€ ํ•„์š” ์—†๋‹ค.


์ƒ์„ฑ์ž ๋ฐฉ์‹์€ @QueryProjection๊นŒ์ง€ ์ง€์›ํ•ด์ค€๋‹ค.

@QueryProjection ํ™œ์šฉ

List<MemberDto> result = queryFactory
          .select(new QMemberDto(member.username, member.age))
          .from(member)
          .fetch();

@QueryProjection ์–ด๋…ธํ…Œ์ด์…˜์„ ๋‹ฌ์•„์ฃผ๊ณ , compileQuerydsl์„ ์‹คํ–‰ํ•ด์ฃผ๋ฉด DTO๋„ QํŒŒ์ผ๋กœ ์ƒ์„ฑ์„ ํ•ด์ค€๋‹ค.

Contructor๋Š” ์ปดํŒŒ์ผ ์˜ค๋ฅ˜๋ฅผ ์žก์ง€ ๋ชปํ•˜๊ณ  ๋Ÿฐํƒ€์ž„ ์˜ค๋ฅ˜๊ฐ€ ์ผ์–ด๋‚˜๊ธฐ์— ์œ ์ €๊ฐ€ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋Š” ์ˆœ๊ฐ„ ๋ฌธ์ œ๋ฅผ ๋ฐœ๊ฒฌํ•  ์ˆ˜ ์žˆ์ง€๋งŒ,

QueryProjection์€ ์ปดํŒŒ์ผ๋Ÿฌ๋กœ ํƒ€์ž…์„ ์ฒดํฌํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ๊ฐ€์žฅ ์•ˆ์ „ํ•œ ๋ฐฉ๋ฒ•์ด๋‹ค.

 

ํ•˜์ง€๋งŒ DTO์— QueryDSL ์–ด๋…ธํ…Œ์ด์…˜์„ ์œ ์ง€ํ•ด์•ผ ํ•˜๊ธฐ์— QueryDSL์— ์ข…์†์ ์ธ ์ ๊ณผ DTO๊นŒ์ง€ QํŒŒ์ผ์„ ์ƒ์„ฑํ•ด์•ผ ํ•˜๋Š” ๋‹จ์ ์ด ์žˆ๋‹ค.

 


 

์ •๋ฆฌ

  • Property: setter ์‚ฌ์šฉ, ๊ธฐ๋ณธ ์ƒ์„ฑ์ž ์‚ฌ์šฉ
  • Field: setter ํ•„์š” ์—†์Œ, ๊ธฐ๋ณธ ์ƒ์„ฑ์ž ํ•„์š” ์—†์Œ, ํ•„๋“œ์™€ ๋งคํ•‘
  • Constructor: @AllArgsConstructor ํ•„์š”, setter ํ•„์š” ์—†์Œ

 

Querydsl - ๋ ˆํผ๋Ÿฐ์Šค ๋ฌธ์„œ

Querydsl์€ JPA, JDO, Mongodb ๋ชจ๋“ˆ์—์„œ ์ฝ”๋“œ ์ƒ์„ฑ์„ ์œ„ํ•ด ์ž๋ฐ”6์˜ APT ์–ด๋…ธํ…Œ์ด์…˜ ์ฒ˜๋ฆฌ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•œ๋‹ค. ์ด ์ ˆ์—์„œ๋Š” ์ฝ”๋“œ ์ƒ์„ฑ์„ ์œ„ํ•œ ๋‹ค์–‘ํ•œ ์„ค์ • ์˜ต์…˜๊ณผ APT์— ๋Œ€ํ•œ ๋Œ€์•ˆ์„ ์„ค๋ช…ํ•œ๋‹ค. ๊ธฐ๋ณธ์ ์œผ๋กœ Query

querydsl.com

 

๋ฐ˜์‘ํ˜•