# [Querydsl] Querydsl ์ด๋ž€?
Study Repository

[Querydsl] Querydsl ์ด๋ž€?

by rlaehddnd0422

QueryDsl ์ด๋ž€?

Querydsl์€ ์ •์  ํƒ€์ž…์„ ์ด์šฉํ•ด์„œ SQL๊ณผ ๊ฐ™์€ ์ฟผ๋ฆฌ๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์ฃผ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ž…๋‹ˆ๋‹ค.

Querydsl์€ ์ฟผ๋ฆฌ๋ฅผ type-safeํ•˜๊ฒŒ ๊ฐœ๋ฐœํ•  ์ˆ˜ ์žˆ๊ฒŒ ์ง€์›ํ•˜๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ ์ฃผ๋กœ JPA Query ( JPQL )์—์„œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

 

DSL์€ Domain Specific Language๋กœ ํŠน์ •ํ•œ ํ† ๋ฉ”์ธ์— ์ดˆ์ ์„ ๋งž์ถ˜ ์ œํ•œ์ ์ธ ํ‘œํ˜„๋ ฅ์„ ๊ฐ€์ง„ ์ปดํ“จํ„ฐ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด๋กœ ๋‹จ์ˆœํ•˜๊ณ , ๊ฐ„๊ฒฐํ•ฉ๋‹ˆ๋‹ค.

 

์ด DSL์„ ์ฟผ๋ฆฌ์— ์ ์šฉ์‹œ์ผœ ์ฟผ๋ฆฌ์— ํŠนํ™”๋œ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด๋กœ ์ œ๊ณตํ•œ ๊ฒƒ์ด QueryDSL์ž…๋‹ˆ๋‹ค.

 

๊ธฐ์กด ๋ฐฉ์‹์—๋Š” ์–ด๋–ค ๋ฌธ์ œ๊ฐ€ ์žˆ์—ˆ์„๊นŒ?

 

- Query๋ฅผ ์ง์ ‘ ์ž‘์„ฑํ•ด์•ผ ํ•˜๋Š” SQL Mapper ๋ฐฉ์‹์ด๋‚˜ JPA์—์„œ JPQL์„ ์‚ฌ์šฉํ•ด Query๊ฐ€ ์ž˜๋ชป ์ž‘์„ฑ๋˜๋Š” ๊ฒฝ์šฐ ์ปดํŒŒ์ผ ์‹œ์ ์—์„œ ์ฒดํฌ๊ฐ€ ๋ถˆ๊ฐ€๋Šฅํ•˜๊ณ  ์˜ค์ง ๋Ÿฐํƒ€์ž„ ์‹œ์ ์—์„œ ์ฒดํฌ๋ฅผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๊ฐœ๋ฐœ์ž๊ฐ€ ์˜ค๋ฅ˜๋ฅผ ์ฐพ๋Š”๋ฐ ์–ด๋ ค์›€์ด ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฐ ๋ฌธ์ œ๋Š” 'type-safe ํ•˜์ง€ ์•Š๋‹ค'๊ณ  ๋งํ•ฉ๋‹ˆ๋‹ค.

 

'type-safe ํ•˜๋‹ค' -> ์ปดํŒŒ์ผ ์‹œ์ ์—์„œ ๋ฌธ์ œ๋ฅผ ์žก์„ ์ˆ˜ ์žˆ๋‹ค.

 

[Java] Type Safe๋ž€?

Type Safe(ํƒ€์ž… ์„ธ์ดํ”„) ๋ž€ ๋ง๊ทธ๋Œ€๋กœ ํƒ€์ž…์— ์•ˆ์ •์ ์ธ ๊ฒƒ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. ํƒ€์ž…์— ๋ถˆ์•ˆ์ •์ ์ด๋‹ค ๋ผ๊ณ  ํ•˜๋Š”๊ฒƒ์€ ํƒ€์ž…์„ ํŒ๋ณ„(Type Check) ํ•˜์ง€ ๋ชปํ•ด Runtime ์‹œ ํƒ€์ž…์œผ๋กœ ์ธํ•œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. Typ

dololak.tistory.com

 

- Querydsl์„ ์‚ฌ์šฉํ•˜๋ฉด ์ฟผ๋ฆฌ๋ฅผ ๋ฉ”์†Œ๋“œ๋กœ ์ œ์–ดํ•ด์„œ ์ปดํŒŒ์ผ ์‹œ์ ์—์„œ ์ฟผ๋ฆฌ์˜ ์˜ค๋ฅ˜๋ฅผ ์žก์•„์ค„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์•ž์„œ ์‚ดํŽด๋ณธ Spring Data JPA์—๋Š” ( ๋™์ ์ธ ์ฟผ๋ฆฌ์— ๋Œ€ํ•œ ) ์กฐํšŒ์—์„œ ์•ฝ์ ์ด ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

Querydsl๋กœ ๋ณต์žกํ•œ ์กฐํšŒ ๊ธฐ๋Šฅ์„ ๋ณด์™„ํ•˜๊ณ  ๊ทธ ์™ธ ๋‹จ์ˆœํ•œ ๊ฒฝ์šฐ๋Š” Spring Data JPA๋ฅผ ์‚ฌ์šฉํ•ฉ์‹œ๋‹ค.


QueryDSL - JPA ๋™์ž‘ 

Querydsl์€ @Entity ์–ด๋…ธํ…Œ์ด์…˜์ด ๋ถ™์€ ๋„๋ฉ”์ธ์„ ์ฝ์–ด Query Domain์„ ์ƒ์„ฑํ•ด์ค๋‹ˆ๋‹ค. (ex) Item -> QItem, Member - > QMember) 

JPAQueryFactory query = new JPAQueryFactory (entityManager); 
QMember m = QMember.member;

List<Member> list = query.select(m)
                    	.from(m) 
                        .where(m.age.between(20, 40).and(m.name.like("๊น€%")))
                        .orderBy(m.age.desc()) .limit(3)
                        .fetch(m);

EntityManager๋ฅผ ์ฃผ์ž…๋ฐ›์•„ JPAQueryFactory ๊ฐ์ฒด๋ฅผ ํ•˜๋‚˜ ๋งŒ๋“ค๊ณ , ๊ฐ์ฒด์˜ ๋ฉ”์†Œ๋“œ๋ฅผ ํ†ตํ•ด ์ฟผ๋ฆฌ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ฉ”์†Œ๋“œ๋ฅผ ํ†ตํ•ด์„œ ์ฟผ๋ฆฌ๋ฅผ ๋งŒ๋“ค๊ฒŒ ๋˜๋ฉด type-safeํ•œ ์ด์ ์„ ์–ป์Šต๋‹ˆ๋‹ค. (ex) ๋ฉ”์†Œ๋“œ์˜ ์ด๋ฆ„์„ ์ž˜๋ชป ์ž‘์„ฑํ•˜๋Š” ๊ฒฝ์šฐ ์ปดํŒŒ์ผ ์‹œ์ ์—์„œ ์—๋Ÿฌ๋ฅผ ์žก์•„์ค๋‹ˆ๋‹ค)

  • fetch() : ๋ชฉ๋ก ์กฐํšŒ
  • fetchOne() : ๋‹จ๊ฑด ์กฐํšŒ

์ƒ์„ฑ๋œ ์ฟผ๋ฆฌ

select id, age, name
from MEMBER
where age between 20 and 40 and name like '๊น€%'
order by age desc
limit 3

 

๊ธฐ๋Šฅ 

  • from
  • innerJoin, join, leftjoin, fullJoin, on
  • where (and,or,allOf,anyOf)
  • groupBy
  • having
  • orderBy(desc,asc)
  • limit, offset, restrict 

 

Querydsl ์„ค์ •

dependencies
{
// Querydsl ์ถ”๊ฐ€
implementation 'com.querydsl:querydsl-jpa'
annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jpa"
annotationProcessor "jakarta.annotation:jakarta.annotation-api"
annotationProcessor "jakarta.persistence:jakarta.persistence-api"

//Querydsl ์ถ”๊ฐ€, ์ž๋™ ์ƒ์„ฑ๋œ Qํด๋ž˜์Šค gradle clean์œผ๋กœ ์ œ๊ฑฐ 
}

clean {
    delete file('src/main/generated')
}

 

Q ํŒŒ์ผ ์ƒ์„ฑ ํ™•์ธ

1. Gradle -> Tasks -> build -> clean
2. Gradle -> Tasks -> other -> compileJava
build -> generated - > sources -> annotationProcessor -> java/main ํ•˜์œ„ ํ™•์ธ 

+ ์ฐธ๊ณ : Qํƒ€์ž…์€ ์ปดํŒŒ์ผ ์‹œ์ ์— ์ž๋™ ์ƒ์„ฑ๋˜๋ฏ€๋กœ ๋ฒ„์ „๊ด€๋ฆฌ(GIT)์— ํฌํ•จํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.


Querydsl ์ ์šฉ

@Repository
@Transactional
public class JpaItemRepositoryV3 implements ItemRepository {

    private final EntityManager em;
    private final JPAQueryFactory query;

    public JpaItemRepositoryV3(EntityManager em)
    {
        this.em = em;
        this.query = new JPAQueryFactory(em);
    }
    ...
  • Querydsl์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด JPAQueryFactory๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. JPAQueryFactory๋Š” JPA ์ฟผ๋ฆฌ์ธ JPQL์„ ๋งŒ๋“ค๊ธฐ ๋•Œ๋ฌธ์— EntityManager๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.
  • ๋ฌผ๋ก  JPAQueryFactory๋ฅผ ์Šคํ”„๋ง ๋นˆ์œผ๋กœ ๋“ฑ๋กํ•ด์„œ ์‚ฌ์šฉํ•ด๋„ ๋ฉ๋‹ˆ๋‹ค.
@Override
public Item save(Item item) {
    em.persist(item);
    return item;
}

@Override
public void update(Long itemId, ItemUpdateDto updateParam) {
    Item findItem = em.find(Item.class,itemId);
    findItem.setItemName(updateParam.getItemName());
    findItem.setPrice(updateParam.getPrice());
    findItem.setQuantity(updateParam.getQuantity());
}

@Override
public Optional<Item> findById(Long id) {
    Item item = em.find(Item.class, id);
    return Optional.ofNullable(item);
}
  • save(), update(), findById() ๊ฐ™์€ ๊ธฐ๋ณธ ๊ธฐ๋Šฅ๋“ค์€ JPA๊ฐ€ ์ œ๊ณตํ•˜๋Š” ๊ธฐ๋ณธ ๊ธฐ๋Šฅ ์‚ฌ์šฉ

Querydsl์„ ์ ์šฉํ•ด ๋™์  ์ฟผ๋ฆฌ ๋ฌธ์ œ ํ•ด๊ฒฐ

public List<Item> findAllOld(ItemSearchCond cond)
{
    String itemName = cond.getItemName();
    Integer maxPrice = cond.getMaxPrice();
    
    QItem item = QItem.item;
    BooleanBuilder builder = new BooleanBuilder();

    if(StringUtils.hasText(itemName))
    {
        builder.and(item.itemName.like('%'+itemName+'%'));
    }
    if(maxPrice!=null)
    {
        builder.and(item.price.loe(maxPrice));
    }

    List<Item> results = query.select(item)
            .from(item)
            .where(builder)
            .fetch();

    return results;
}
  • BooleanBuilder๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์›ํ•˜๋Š” where์กฐ๊ฑด๋“ค์„ ๋„ฃ์–ด์ค๋‹ˆ๋‹ค.

 

๋” ๊น”๋”ํ•˜๊ฒŒ ๋ฆฌํŽ™ํ† ๋ง

 

@Override
public List<Item> findAll(ItemSearchCond cond)
{
    String itemName = cond.getItemName();
    Integer maxPrice = cond.getMaxPrice();

    return query.select(item)
            .from(item)
            .where(likeItemName(itemName), maxPrice(maxPrice))
            .fetch();
}
  • 1) Qitem static import -> item
  • Querydsl์—์„œ where(A,B)์— ๋‹ค์–‘ํ•œ ์กฐ๊ฑด๋“ค์„ ์ง์ ‘ ๋„ฃ์„ ์ˆ˜ ์žˆ๋Š”๋ฐ, ์ด๋ ‡๊ฒŒ ๋„ฃ์œผ๋ฉด and๋กœ ์ฒ˜๋ฆฌ๋ฉ๋‹ˆ๋‹ค. ์กฐ๊ฑด์ด null์ธ ๊ฒฝ์šฐ ํ•ด๋‹น ์กฐ๊ฑด ๋ฌด์‹œ

+ config ์„ค์ •

@Configuration
  @RequiredArgsConstructor
  public class QuerydslConfig {
      private final EntityManager em;
      
      @Bean
      public ItemService itemService() {
          return new ItemServiceV1(itemRepository());
      }
      
      @Bean
      public ItemRepository itemRepository() {
          return new JpaItemRepositoryV3(em);
      }
}

<์ •๋ฆฌ>

  • Querydsl์„ ์‚ฌ์šฉํ•ด ๋™์  ์ฟผ๋ฆฌ๋ฅผ ๊น”๋”ํ•˜๊ฒŒ ์ฒ˜๋ฆฌํ•ด๋ณด์•˜์Šต๋‹ˆ๋‹ค.
  • Querydsl์„ ์‚ฌ์šฉํ•˜๋ฉด type-safeํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๊ฐœ๋ฐœ์ž ์ž…์žฅ์—์„œ๋Š” ์˜ค๋ฅ˜๋ฅผ ์ฐพ๊ธฐ ์‰ฝ์Šต๋‹ˆ๋‹ค.
  • where๋ฌธ ๋‚ด์— ๋ฉ”์†Œ๋“œ๋กœ ์กฐ๊ฑด์„ ๋งŒ๋“ค์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ๋‹ค๋ฅธ ๋™์  ์ฟผ๋ฆฌ์—์„œ๋„ ์žฌ์‚ฌ์šฉ์˜ ์—ฌ์ง€๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

<์ฐธ๊ณ ์ž๋ฃŒ>

 

์Šคํ”„๋ง DB 2ํŽธ - ๋ฐ์ดํ„ฐ ์ ‘๊ทผ ํ™œ์šฉ ๊ธฐ์ˆ  - ์ธํ”„๋Ÿฐ | ๊ฐ•์˜

๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ์— ํ•„์š”ํ•œ DB ๋ฐ์ดํ„ฐ ์ ‘๊ทผ ๊ธฐ์ˆ ์„ ํ™œ์šฉํ•˜๊ณ , ์™„์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์Šคํ”„๋ง DB ์ ‘๊ทผ ๊ธฐ์ˆ ์˜ ์›๋ฆฌ์™€ ๊ตฌ์กฐ๋ฅผ ์ดํ•ดํ•˜๊ณ , ๋” ๊นŠ์ด์žˆ๋Š” ๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ์ž๋กœ ์„ฑ์žฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค., - ๊ฐ•์˜ ์†Œ๊ฐœ | ์ธ

www.inflearn.com

 

๋ธ”๋กœ๊ทธ์˜ ์ •๋ณด

Study Repository

rlaehddnd0422

ํ™œ๋™ํ•˜๊ธฐ