query dsl

이민지·2023년 1월 8일

DB

목록 보기
4/5

설정

  1. build.gradle설정
    아래 코드에서 query dsl추가라고 되어 있는 부분만 build.gradle에 추가해준다.
// query dsl 추가
buildscript {
	ext {
		queryDslVersion = "5.0.0"
	}
}

plugins {
	id 'java'
	id 'org.springframework.boot' version '2.7.7'
	id 'io.spring.dependency-management' version '1.0.15.RELEASE'
	//querydsl 추가
	id "com.ewerk.gradle.plugins.querydsl" version "1.0.10"
}

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

configurations {
	compileOnly {
		extendsFrom annotationProcessor
	}
}

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
	implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	implementation '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'
	implementation group: 'org.mariadb.jdbc', name: 'mariadb-java-client', version: '2.7.6'
	implementation 'com.github.gavlyukovskiy:p6spy-spring-boot-starter:1.5.6'
	implementation 'org.springframework.boot:spring-boot-starter-validation'

	// querydsl 추가
	implementation "com.querydsl:querydsl-jpa:${queryDslVersion}" // querydsl 라이브러리
	annotationProcessor "com.querydsl:querydsl-apt:${queryDslVersion}" //  Querydsl 관련 코드 생성 기능 제공

}

tasks.named('test') {
	useJUnitPlatform()
}

// querydsl 추가
def querydslDir = "$buildDir/generated/querydsl"
querydsl {
	jpa = true
	querydslSourcesDir = querydslDir
}

sourceSets { // IDE의 소스 폴더에 자동으로 넣어준다.
	main.java.srcDir querydslDir
}

configurations {
	querydsl.extendsFrom compileClasspath // 컴파일이 될때 같이 수행
}

compileQuerydsl {
	options.annotationProcessorPath = configurations.querydsl // Q파일을 생성해준다.
}
  1. gradle -> compileQuerydsl실행

compileQuerydsl을 실행하면 아래 사진처럼 querydsl 폴더가 생성된다.

  1. 동적 쿼리 작성하기
// order 엔티티 작성
@Entity
@Table(name="orders")
@Getter @Setter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Order {
    @Id @GeneratedValue
    @Column(name="order_id")
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name="member_id")
    private Member member;

    @OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
    private List<OrderItem> orderItems = new ArrayList<>();

    @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JoinColumn(name = "delivery_id")
    private Delivery delivery;

    private LocalDateTime orderDate;

    @Enumerated(EnumType.STRING)
    private OrderStatus status;
}

// order repo에서 동적 쿼리 작성
@Repository
@RequiredArgsConstructor
public class OrderRepository {
	private final EntityManager em;
    
    // OrderSearch에는 필터링할 두개의 필드가 담겨있음(orderStatus, memberName)
    public List<Order> findAllQueryDsl(OrderSearch orderSearch) {
        JPAQueryFactory queryFactory = new JPAQueryFactory(em);

        QOrder o = QOrder.order;

        BooleanBuilder builder = new BooleanBuilder();

		
        // 필터링할 orderStatus 값이 전달되었다면, builder에 설정
        if (orderSearch.getOrderStatus() != null) {
        	// order객체의 status 값과 주어진 orderStatus가 같은지 확인
            builder.and(o.status.eq(orderSearch.getOrderStatus()));
        }
		
        // 필터링할 memberName의 값이 null이 아니고 공백이 아니라면, builder설정
        if (orderSearch.getMemberName() != null && !orderSearch.getMemberName().trim().isEmpty()) {
        	// order객체에 member객체의 이름이 memberName이랑 같은지 확인
            builder.and(o.member.name.eq(orderSearch.getMemberName()));
        }
		
        // queryFactory를 통해서 쿼리를 생성
        // selectFrom(o)는 select * from order와 같은 의미
        // where(builder)는 위에서 설정한 조건들을 반영하는 레코드만 선별하는 것
        // fetch는 리스트 조회 
        List<Order> orders = queryFactory.selectFrom(o)
                .where(builder)
                .fetch();
        return orders;
    }
}

동적쿼리를 생성할 때, query dsl을 사용하는 이유

jpal의 경우 string이기 때문에 동적 쿼리를 생성하기 위해서는 조건에 따라서 string을 +로 계속 이어붙어야 한다. 따라서 조건이 몇개 없다면 가능하긴 하겠지만, 조건이 많아지면 코드도 굉장히 길어지고 사실상 불가능에 가까울만큼 복잡해진다. 유지보수 측면에서 어려운 것은 말할 필요도 없다.

또한 spring에서 정식적은 방법으로 선택한 JPA Criteria의 경우 jpal의 경우보다는 훨씬 쉽게 동적 쿼리가 생성이 가능하긴 하지만, 유지보수 측면에서 불리하다.

반면에 query dsl은 코드로 조건을 이어붙이는 방식이기 때문에 코드 작성과 유지보수가 쉽고, 컴파일 단계에서 오류를 발견할 수 있기 때문에 좋다.

[참고] https://ict-nroo.tistory.com/117
[참고 - query dsl 1, 2, 3편] https://devfunny.tistory.com/849

profile
BE 개발자를 희망하는 주니어 개발자입니다.

0개의 댓글