티스토리 뷰

0. 프로젝트 구조

 

1. build.gradle

1) buildscript

//querydsl 추가
buildscript {
	dependencies {
		classpath("gradle.plugin.com.ewerk.gradle.plugins:querydsl-plugin:1.0.10")
	}
}

 

2) apply

//querydsl 추가
apply plugin: "com.ewerk.gradle.plugins.querydsl"

 

3) dependencies

//querydsl 추가
implementation 'com.querydsl:querydsl-jpa'
implementation 'com.querydsl:querydsl-apt'
//querydsl 추가 끝

 

4) Querydsl build 관련 추가 설정

//querydsl 추가
def querydslDir = "$buildDir/generated/querydsl"

querydsl {
	library = "com.querydsl:querydsl-apt"
	jpa = true
	querydslSourcesDir = querydslDir
}

sourceSets {
	main {
		java {
			srcDirs = ['src/main/java', querydslDir]
		}
	}
}

compileQuerydsl{
	options.annotationProcessorPath = configurations.querydsl
}

configurations {
	querydsl.extendsFrom compileClasspath
}

/* comileQuerydsl.doFirst 추가: 재컴파일 시 q파일을 모두 지우도록 */
compileQuerydsl.doFirst {
	if(file(querydslDir).exists())
		delete(file(querydslDir))
}
//querydsl 추가 끝

 

4) 전체 코드

//querydsl 추가
buildscript {
	dependencies {
		classpath("gradle.plugin.com.ewerk.gradle.plugins:querydsl-plugin:1.0.10")
	}
}

plugins {
	id 'org.springframework.boot' version '2.6.0'
	id 'io.spring.dependency-management' version '1.0.11.RELEASE'
	id 'java'
}

group = 'mandykr'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'

//querydsl 추가
apply plugin: "com.ewerk.gradle.plugins.querydsl"

configurations {
	compileOnly {
		extendsFrom annotationProcessor
	}
}

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
	implementation 'org.springframework.boot:spring-boot-starter-security'
	implementation 'org.springframework.boot:spring-boot-starter-validation'
	implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5'

	// jpa binding parameter log 확인
	implementation("com.github.gavlyukovskiy:p6spy-spring-boot-starter:1.7.1")

	// html 컴파일로 thymeleaf 변경 반영
	runtimeOnly 'org.springframework.boot:spring-boot-devtools'

	compileOnly 'org.projectlombok:lombok'
	runtimeOnly 'com.h2database:h2'
	annotationProcessor 'org.projectlombok:lombok'

	testImplementation 'org.springframework.boot:spring-boot-starter-test'
	testImplementation 'org.springframework.security:spring-security-test'

	//querydsl 추가
	implementation 'com.querydsl:querydsl-jpa'
	implementation 'com.querydsl:querydsl-apt'
	//querydsl 추가 끝
}

test {
	useJUnitPlatform()
}

//querydsl 추가
def querydslDir = "$buildDir/generated/querydsl"

querydsl {
	library = "com.querydsl:querydsl-apt"
	jpa = true
	querydslSourcesDir = querydslDir
}

sourceSets {
	main {
		java {
			srcDirs = ['src/main/java', querydslDir]
		}
	}
}

compileQuerydsl{
	options.annotationProcessorPath = configurations.querydsl
}

configurations {
	querydsl.extendsFrom compileClasspath
}

/* comileQuerydsl.doFirst 추가: 재컴파일 시 q파일을 모두 지우도록 */
compileQuerydsl.doFirst {
	if(file(querydslDir).exists())
		delete(file(querydslDir))
}
//querydsl 추가 끝

 

2. JPAQueryFactory Bean 등록

@Configuration
public class QueryDslConfig {

    @PersistenceContext
    private EntityManager entityManager;

    @Bean
    public JPAQueryFactory jpaQueryFactory() {
        return new JPAQueryFactory(entityManager);
    }
}

 

3. Querydsl 컴파일

 - gradle 빌드 혹은 other의 compileQuerydsl 더블클릭

 

4. Querydsl 이 컴파일되면 다음 경로에 Q타입 entity가 생성됨

Q 파일은 git에서 관리하면 안됨 (build 폴더는 기본 git 대상에 제외되어 있음)

 

5. SupplementsDto 생성

package mandykr.nutrient.dto;

import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
public class SupplementsDto {
    private Long id;
    private String name;
}

 

6. SupplementsSearchCondition 생성

package mandykr.nutrient.dto;

import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
public class SupplementsSearchCondition {
    private Long id;
    private String name;

    public SupplementsSearchCondition(Long id) {
        this.id = id;
    }

    public SupplementsSearchCondition(String name) {
        this.name = name;
    }

    public SupplementsSearchCondition(Long id, String name) {
        this.id = id;
        this.name = name;
    }
}

 

7. SupplementsRepositoryCustom 인터페이스 생성

Querydsl 사용 repository 인터페이스

package mandykr.nutrient.repository;

import mandykr.nutrient.dto.SupplementsDto;
import mandykr.nutrient.dto.SupplementsSearchCondition;

import java.util.List;

public interface SupplementsRepositoryCustom {
    List<SupplementsDto> search(SupplementsSearchCondition condition);
}

 

8. SupplementsRepositoryImpl 생성

Querydsl 사용 repository

package mandykr.nutrient.repository;

import com.querydsl.core.types.Projections;
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.jpa.impl.JPAQueryFactory;
import mandykr.nutrient.dto.SupplementsDto;
import mandykr.nutrient.dto.SupplementsSearchCondition;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import javax.persistence.EntityManager;
import java.util.List;

import static mandykr.nutrient.entity.QSupplements.*;

@Repository
public class SupplementsRepositoryImpl implements SupplementsRepositoryCustom {
    private final JPAQueryFactory queryFactory;

    @Autowired
    public SupplementsRepositoryImpl(EntityManager em) {
        this.queryFactory = new JPAQueryFactory(em);
    }

    @Override
    public List<SupplementsDto> search(SupplementsSearchCondition condition) {
        List<SupplementsDto> result = queryFactory
                .select(Projections.fields(SupplementsDto.class,
                        supplements.id,
                        supplements.id))
                .from(supplements)
                .where(supplementsNameEq(condition.getName()))
                .fetch();

        return result;
    }

    // supplementsName 비교 조건절
    public BooleanExpression supplementsNameEq(String supplementsNameCond) {
        return supplements.name.eq(String.valueOf(supplementsNameCond));
    }
}

조회 조건을 SupplementsSearchCondition 에 담아 전달

조회 결과를 SupplementsDto list 에 담아 반환

 

9. SupplementsRepository 인터페이스가 SupplementsRepositoryCustom 인터페이스를 상속받도록 연결

SupplementsRepository 참조변수로

Spring Data Jpa 와 SupplementsRepositoryImpl 클래스에서 Querydsl 구현한 메소드를 모두 사용할 수 있다.

package mandykr.nutrient.repository;

import mandykr.nutrient.entity.Supplements;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.List;

public interface SupplementsRepository extends JpaRepository<Supplements, Long>, SupplementsRepositoryCustom {
    List<Supplements> findByName(String name);
}

 

10. 테스트 코드 작성

package mandykr.nutrient;

import mandykr.nutrient.dto.SupplementsDto;
import mandykr.nutrient.dto.SupplementsSearchCondition;
import mandykr.nutrient.entity.Supplements;
import mandykr.nutrient.repository.SupplementsRepository;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.Commit;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;

@SpringBootTest
@Transactional
class SupplementsRepositoryTests {
	@Autowired
	SupplementsRepository supplementsRepository;

	@Test
	@Commit
	void save() {
		Supplements supplements = new Supplements();
		supplements.setName("빌베리 플러스");
		SupplementsSearchCondition condition = new SupplementsSearchCondition(supplements.getName());

		Supplements saveSupplements = supplementsRepository.save(supplements);
		List<Supplements> supplementsListSDJpa = supplementsRepository.findByName(supplements.getName());
		List<SupplementsDto> supplementsListQdsl = supplementsRepository.search(condition);

		assertThat(supplementsListSDJpa.size()).isEqualTo(supplementsListQdsl.size()).isEqualTo(1);
	}

}

 

 

출처

https://www.inflearn.com/course/Querydsl-실전 실전! Querydsl(김영한)

728x90