๐Ÿ“Œ ์‚ฌ์šฉ ๊ณ„๊ธฐ

๋ฐ์ดํ„ฐ ๊ฒ€์ƒ‰ ์„ฑ๋Šฅ ๊ฐœ์„ ์„ ํ•ด๋ณด๋Š” ํ”„๋กœ์ ํŠธ์—์„œ ๊ฒ€์ƒ‰ ์ƒํƒœ์— ๋”ฐ๋ผ ๋‹ค๋ฅธ JPQL๋ฌธ์„ ์ƒ์„ฑํ•  ํ•„์š”๊ฐ€ ์žˆ์–ด QueryDSL์„ ์ ์šฉํ•˜๊ฒŒ ๋˜์—ˆ๋‹ค. 

 


๐Ÿ“QueryDSL์ด๋ž€?

JPQL๋ฌธ์„ ๋ฌธ์ž์—ด์ด ์•„๋‹Œ ์ž๋ฐ” ์ฝ”๋“œ๋กœ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋„๋ก ๋นŒ๋” ์—ญํ• ์„ ํ•˜๋Š” ์˜คํ”ˆ ์†Œ์Šค

 


๐Ÿ“QueryDSL ํŠน์ง•

  • ์ฟผ๋ฆฌ๋ฅผ ์ฝ”๋“œ๋กœ ์ž‘์„ฑํ•ด ์ปดํŒŒ์ผ ์‹œ ์˜ค๋ฅ˜ ๋ฐœ๊ฒฌ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.
  • ์ฝ”๋“œ ๊ธฐ๋ฐ˜์— ๋‹จ์ˆœํ•˜์—ฌ ์‚ฌ์šฉ์ด ์‰ฝ๊ณ  ๊ฐ€๋…์„ฑ์ด ์ข‹์Œ
  • ์ž๋™ ์™„์„ฑ ๋“ฑ IDE์˜ ๋„์›€ ๊ฐ€๋Šฅ
  • ๋™์  ์ฟผ๋ฆฌ ๊ตฌํ˜„ ๊ฐ€๋Šฅ

 


๐Ÿ“์ ์šฉ ๋ฐฉ๋ฒ•

  • build.gradle
buildscript {						// gradle๋กœ task๋ฅผ ์ˆ˜ํ–‰ํ•  ๋•Œ์— ์‚ฌ์šฉ๋˜๋Š” ์„ค์ •
	ext {							     // build.gradle์—์„œ ์‚ฌ์šฉํ•˜๋Š” ์ „์—ญ ๋ณ€์ˆ˜ ์„ค์ •
		queryDslVersion = "5.0.0"	// ๋ณ€์ˆ˜ queryDslVersion์— 5.0.0 ๋Œ€์ž…
	}
}

plugins {							// gradle task์˜ ์ง‘ํ•ฉ
	//....//
	id 'com.ewerk.gradle.plugins.querydsl' version '1.0.10'	// querydsl plugin ์ ์šฉ
}

dependencies {
	//....//

	// querydsl ์˜์กด์„ฑ ์ถ”๊ฐ€
  // buildscript์—์„œ ์ ์šฉํ•œ ๋ณ€์ˆ˜ queryDslVersion์ด ์—ฌ๊ธฐ์— ์ ์šฉ
	implementation "com.querydsl:querydsl-jpa:${queryDslVersion}"	
	implementation "com.querydsl:querydsl-apt:${queryDslVersion}"
}

// ๋ณ€์ˆ˜ ์„ ์–ธ, ๊ทธ ๋ณ€์ˆ˜์— ํด๋” ๊ฒฝ๋กœ ์ €์žฅ
def querydslDir = "$buildDir/generated/querydsl"			

querydsl {															
	jpa = true														// JPA ์‚ฌ์šฉ์—ฌ๋ถ€ ์„ค์ •
	querydslSourcesDir = querydslDir		  // ์‚ฌ์šฉํ•  ๊ฒฝ๋กœ ์„ค์ •
}

sourceSets {														// build์‹œ ์‚ฌ์šฉํ•  sourceSet ์ถ”๊ฐ€
	main.java.srcDir querydslDir
}

compileQuerydsl {												// queryDSL ์ปดํŒŒ์ผ์‹œ ์‚ฌ์šฉํ•  ์˜ต์…˜ ์„ค์ •
	options.annotationProcessorPath = configurations.querydsl
}

configurations {												// build ์˜ต์…˜
	compileOnly {
		extendsFrom annotationProcessor
	}
   // querydsl์ด compileClasspath๋ฅผ ์ƒ์†ํ•˜๋„๋ก ์„ค์ •
	querydsl.extendsFrom compileClasspath	 
}
  • config
@Configuration
public class QuerydslConfig {

    @PersistenceContext
    // @PersistenceContext๋กœ ์ฃผ์ž…๋ฐ›์€ Entity Manager์€ Proxy๋กœ ๊ฐ์‹ธ์ง„๋‹ค.
    // EntityManager ํ˜ธ์ถœ์‹œ ๋งˆ๋‹ค Proxy๋ฅผ ํ†ตํ•ด EntityManager์„ ์ƒ์„ฑํ•˜์—ฌ Thread-Safe๋ฅผ ๋ณด์žฅ
    private EntityManager entityManager;

    @Bean
    // JPAQuery๋ฅผ ์ƒ์„ฑํ•ด์ฃผ๋Š” factoryํด๋ž˜์Šค
    public JPAQueryFactory jpaQueryFactory() {
        return new JPAQueryFactory(entityManager);
    }
}
  • Q-Class

QueryDSL์—์„œ๋Š” Entity๋กœ ์„ค์ •๋œ ํด๋ž˜์Šค์— Q๋ชจ๋ธ์˜ ์ฟผ๋ฆฌ ํƒ€์ž… ํด๋ž˜์Šค๋ฅผ ๋ฏธ๋ฆฌ ์ƒ์„ฑํ•˜๊ณ  ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋กœ ์‚ฌ์šฉํ•˜์—ฌ ์ฟผ๋ฆฌ๋ฅผ ๋ฉ”์†Œ๋“œ ๊ธฐ๋ฐ˜์œผ๋กœ ์ž‘์„ฑํ•œ๋‹ค.

 

์ƒ์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•์€ Tool Window Bar (์‚ฌ์ด๋“œ์— ๋ณด์ด๋Š” ๋ฉ”๋‰ด๋ช…)์—์„œ Gradle > other > compileJava ํด๋ฆญ

์ƒ์„ฑ๋œ ๊ฒƒ์„ ํ™•์ธ

 

 


 

๐Ÿ“ QueryDSL ๊ตฌํ˜„ ๋ฐฉ๋ฒ• (์ด 3๊ฐ€์ง€)

  • ๋ฐฉ๋ฒ• 1 )  QuerydslRepositorySupport๋ฅผ ์ƒ์† ๋ฐ›์•„ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•
    • ๊ตฌํ˜„์ฝ”๋“œ
      • JpaRepository๋ฅผ ์ƒ์† ๋ฐ›๋Š” ProductRepository ์ธํ„ฐํŽ˜์ด์Šค ํ•˜๋‚˜
        import com.example.showmethemany.domain.Products;
        import org.springframework.data.jpa.repository.JpaRepository;
        
        public interface ProductRepository extends JpaRepository<Products, Long> {
        }
      • QuerydslRepositorySupport๋ฅผ ์ƒ์†ํ•˜๊ณ  Super์ƒ์„ฑ์ž์— Entity๋ฅผ ์ง€์ •ํ•ด์„œ ๋„ฃ์€ ํด๋ž˜์Šค ํ•˜๋‚˜(ProductRepositorySupport)
        import com.example.showmethemany.domain.Products;
        import com.querydsl.jpa.impl.JPAQueryFactory;
        import org.springframework.data.jpa.repository.support.QuerydslRepositorySupport;
        import org.springframework.stereotype.Repository;
        
        import static com.example.showmethemany.domain.QProducts.products;
        
        @Repository
        public class ProductRepositorySupport extends QuerydslRepositorySupport {
        
            private final JPAQueryFactory queryFactory;
            public ProductRepositorySupport(JPAQueryFactory queryFactory) {
                super(Products.class);
                this.queryFactory = queryFactory;
            }
        
            public Products findByProductId (Long productId) {
                return queryFactory.select(products)
                        .from(products)
                        .where(products.id.eq(productId))
                        .fetchOne();
            }
        }
    • ํ…Œ์ŠคํŠธ์ฝ”๋“œ
      import com.example.showmethemany.domain.Products;
      import org.junit.jupiter.api.DisplayName;
      import org.junit.jupiter.api.Test;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.boot.test.context.SpringBootTest;
      
      import static org.junit.jupiter.api.Assertions.*;
      
      @SpringBootTest
      class ProductRepositorySupportTest {
      
          @Autowired
          ProductRepository productRepository;
      
          @Autowired
          ProductRepositorySupport productRepositorySupport;
      
          @Test
          @DisplayName("QueryDSL Support ์ƒ์† ๊ตฌํ˜„ ๋ฐฉ์‹ ํ…Œ์ŠคํŠธ")
          void findByProductId() {
              //given
              Long productId = 1L;
      
              //when
              Products products = productRepositorySupport.findByProductId(productId);
      
              //then
              assertEquals(26910, products.getPrice());
          }
      }
    • ๊ฒฐ๊ณผ
      ๊ทธ๋Ÿฌ๋‚˜ ์ด ๊ตฌํ˜„ ๋ฐฉ๋ฒ•์˜ ๋ฌธ์ œ์ ์€ 
      QuerydslRepositorySupport(์ฝ”๋“œ์ƒ ProductRepositorySupport)์™€ JpaRepository(์ฝ”๋“œ์ƒ ProductRepository)๊ฐ€ ๊ธฐ๋Šฅ์„ ๋‚˜๋ˆ  ๊ฐ€์ ธ ํ•ญ์ƒ 2๊ฐœ๋ฅผ ์˜์กด์„ฑ์œผ๋กœ ๋ฐ›์•„์•ผ ํ•˜๋Š” ๋ฌธ์ œ๊ฐ€ ์žˆ๋‹ค
     
  • ๋ฐฉ๋ฒ• 2 ) CustomRepository ์ƒ์†๊ณผ ๊ตฌํ˜„์ด ํ•„์š”ํ•œ ๋ฐฉ๋ฒ•
    • ๊ตฌํ˜„์ฝ”๋“œ
      • CustomRepository ์ธํ„ฐํŽ˜์ด์Šค ํ•˜๋‚˜
        import com.example.showmethemany.domain.Product
        
        public interface ProductRepositoryCustom {
            Products findByProductId (Long productId);
        }
      • CustomRepository ์˜ ๊ตฌํ˜„์ฒด ํ•˜๋‚˜
        import com.example.showmethemany.domain.Products;
        import com.querydsl.jpa.impl.JPAQueryFactory;
        import javax.persistence.EntityManager;
        import static com.example.showmethemany.domain.QProducts.products;
        
        public class ProductRepositoryImpl implements ProductRepositoryCustom {
        
            private final JPAQueryFactory queryFactory;
        
            public ProductRepositoryImpl(EntityManager em) {
                this.queryFactory = new JPAQueryFactory(em);
            }
        
            @Override
            public Products findByProductId(Long productId) {
                return queryFactory.select(products)
                        .from(products)
                        .where(products.id.eq(productId))
                        .fetchOne();
            }
        }
      • JpaRepository ์— CustomRepository๋ฅผ ์ƒ์†ํ•จ์œผ๋กœ์จ CustomRepositoryImpl๋„ ์‚ฌ์šฉ ๊ฐ€๋Šฅ
        import com.example.showmethemany.domain.Products;
        import org.springframework.data.jpa.repository.JpaRepository;
        
        public interface ProductRepository extends JpaRepository<Products, Long>, ProductRepositoryCustom {
        }
    • ํ…Œ์ŠคํŠธ์ฝ”๋“œ
      @SpringBootTest
      class ProductRepositoryTest {
      
          @Autowired
          ProductRepository productRepository;
      
          @Test
          @DisplayName("QueryDSL CustomRepository ์ƒ์† ๋ฐฉ์‹ ํ…Œ์ŠคํŠธ")
          void findByProductId() {
              //given
              Long productId = 1L;
      
              //when
              Products products = productRepository.findByProductId(productId);
      
              //then
              assertEquals(26910, products.getPrice());
          }
      }
    • ๊ฒฐ๊ณผ
      ๊ทธ๋Ÿฌ๋‚˜ ์ด ๊ตฌํ˜„ ๋ฐฉ๋ฒ•์˜ ๋ฌธ์ œ์ ์€ 
      ํ•˜๋‚˜์˜ Repository๋‹น interfaceํ•˜๋‚˜์™€ Implํ•˜๋‚˜ ์ด 2๊ฐœ๋ฅผ ๋” ๋งŒ๋“ค์–ด์ค˜์•ผ ํ•œ๋‹ค. (์ด 3๊ฐœ)
     
  • ๋ฐฉ๋ฒ• 3 ) ์ƒ์†๊ณผ ๊ตฌํ˜„ ์—†์ด ๊ฐ€๋Šฅํ•œ ๋ฐฉ๋ฒ•
    • ๊ตฌํ˜„์ฝ”๋“œ
      • JPAQueryFactory๋ฅผ ์ฃผ์ž… ๋ฐ›์€ Repository class ํ•˜๋‚˜
        JPAQueryFactory๋งŒ ์ฃผ์ž… ๋ฐ›์œผ๋ฉด QueryDSL์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ณ  ํŠน์ • Entity ์ง€์ •ํ•˜์ง€ ์•Š์•„๋„ ์—ฌ๋Ÿฌ Entity๋ฅผ ๋Œ€์ƒ์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
        import com.example.showmethemany.domain.Products;
        import com.querydsl.jpa.impl.JPAQueryFactory;
        import org.springframework.stereotype.Repository;
        import static com.example.showmethemany.domain.QProducts.products;
        
        @Repository
        public class ProductRepository {
        
            private final JPAQueryFactory queryFactory;
        
            public ProductRepository(JPAQueryFactory queryFactory) {
                this.queryFactory = queryFactory;
            }
        
            public Products findByProductId (Long productId) {
                return queryFactory.select(products)
                        .from(products)
                        .where(products.id.eq(productId))
                        .fetchOne();
            }
        }
    • ํ…Œ์ŠคํŠธ์ฝ”๋“œ
      @SpringBootTest
      class ProductRepositoryTest {
      
          @Autowired
          private ProductRepository productRepository;
      
          @Test
          @DisplayName("QueryDSL ์ƒ์†/๊ตฌํ˜„ ์—†๋Š” ๋ฐฉ์‹ ํ…Œ์ŠคํŠธ")
          void findById() {
              //given
              Long productId = 1L;
      
              //when
              Products products = productRepository.findByProductId(productId);
      
              //then
              assertEquals(26910, products.getPrice());
          }
      }
    • ๊ฒฐ๊ณผ
      ์ด ๊ตฌํ˜„ ๋ฐฉ๋ฒ•์€ ๋‹ค๋ฅธ ๊ฒฝ์šฐ๋ณด๋‹ค ๋ถ€๋‹ด์ด ๋œ๋œ๋‹ค๋Š” ์žฅ์ ์€ ์žˆ์ง€๋งŒ ๊ธฐ๋ณธ JpaRepository์™€๋Š” ๋ณ„๊ฐœ๊ฐ€ ๋˜๊ธฐ ๋•Œ๋ฌธ์— ๊ธฐ์กด Repository์˜ ๋ฉ”์„œ๋“œ๋ฅผ ์ฐธ์กฐํ•  ์ˆ˜ ์—†๊ฒŒ ๋œ๋‹ค. QueryDSL๋งŒ ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜๋ฉด JPA๋กœ ๊ฐ„๋‹จํžˆ ์‚ฌ์šฉํ–ˆ๋˜ ๋ฉ”์†Œ๋“œ๋ฅผ ๊ตฌํ˜„ํ•ด ์ค˜์•ผ ํ•œ๋‹ค๋Š” ๋‹จ์ ์ด ์žˆ๋‹ค. ๊ทธ๋ž˜์„œ ํ”„๋กœ์ ํŠธ์—๋Š” QueryDSL๊ณผ Spring Data JPA๋ฅผ ๊ฐ™์ด ์“ฐ๋Š” ๋ฐฉํ–ฅ์œผ๋กœ ์ง„ํ–‰ํ–ˆ๋‹ค. 

+ Recent posts