Querydsl 설정과 기본 쿼리

KHoney·2022년 6월 29일
0

Java

목록 보기
2/10
post-thumbnail

Querydsl

  • 코드로 JSQL 을 작성할 수 있도록 도와주는 오픈소스 빌더 API
  • JSQL 의 불편함을 보완해준다.
  • JPA 크리테이라에 비해서 편리하고 실용적
  • IDE 를 통한 자동완성
  • 코드를 통한 쿼리를 쉽게 연상할 수 있다.
  • 동적 쿼리를 만들기 쉽다.
  • 컴파일 타임에 문법를 잡을수 있다.
  • Java 타입 매칭이 깔끔하다

설정

pom.xml 에 의존성과 플러그인을 추가한다.

querydsl-jpa: QueryDSL JPA 라이브러리
querydsl-apt: 쿼리 타입(Q)을 생성할 때 필요한 라이브러리

<!-- <https://mvnrepository.com/artifact/com.querydsl/querydsl-jpa> -->
<!-- ${querydsl.vesion 은 spring boot 에 내장 설정 되어있다.-->
<!-- QueryDSL APT Config -->
<dependency>
    <groupId>com.querydsl</groupId>
    <artifactId>querydsl-apt</artifactId>
    <version>${querydsl.version}</version>
    <scope>provided</scope>
</dependency>
<!-- QueryDSL JPA Config -->
<dependency>
    <groupId>com.querydsl</groupId>
    <artifactId>querydsl-jpa</artifactId>
    <version>${querydsl.version}</version>
</dependency>

QueryDSL을 사용하려면 Criteria의 메타 모델처럼 엔티티를 기반으로 쿼리 타입이라는 쿼리용 클래스를 생성해야 한다.

다음과 같이 쿼리 타입 생성용 APT 플러그인을 메이븐에 설정한다.

<plugin>
    <groupId>com.mysema.maven</groupId>
    <artifactId>apt-maven-plugin</artifactId>
    <version>1.1.3</version>
    <executions>
        <execution>
            <goals>
                <goal>process</goal>
            </goals>
            <configuration>
                <outputDirectory>target/generated-sources/java</outputDirectory>
                <processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor>
            </configuration>
        </execution>
    </executions>
</plugin>

maven clean, install 후 target/generated-sources/java 경로에 Query 타입이 자동생성된다.

intall 시에 @Entity 로 선언한 DTO 의 Query 타입이 생성된다.

매번 생성하지 않아도 사용할 수 있도록 JPAQueryFactory 객체를 Bean으로 등록한다.

QueryDslConfig.java

@Configuration
public class QueryDslConfig {
    @PersistenceContext
    private EntityManager entityManager;

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

}

예제

UserDTO.java @Entity를 가지고 QUserDTO.java 라는 Query 타입이 생성된다.

EntityManager 를 JPAQueryFactory에 넣고, QUserDTO 객체를 가지고 아래와 같은 방식으로 쿼리를 코드로 짤 수 있다.

UserDTO

@Entity
@Table(name = "USERS")
public class UserDTO {
    @Id
    @Column(name="id")
    private String id;
    @Column(name="name")
    private String name;
    @Column(name="age")
    private int age;
}

test code

    @Test
    @DisplayName("유저 select 테스트")
    public void readUser() {
    	//given
        QUserDTO qUser = QUserDTO.userDTO; // 기본 인스턴스로 사용
//		QUserDTO qUser = new QUserDTO("myQUser"); 별칭으로 생성
			//when
        var result = query.selectFrom(qUser)
        	.where(qUser.name.like("%test%"))
            .fetch();
      //then
			assertThat(result.size(), is(2));
    }

주의!
Query 타입 객체를 선언할때, 기본적으로는 인스턴스로 사용해도 상관없지만,
같은 엔티티를 조인하거나 같은 엔티티를 서브쿼리에 사용할 때는 별도의 별칭으로 생성 해야한다.

동적 Query

Querydsl 의 장점중 하나

조건문을 아주 쉽고 빠르게 추가/수정 할 수 있다.

나이가 20 이상인 김씨 user만 select 한다고 가정한다면.

int ageMin = 20;
var result = query.selectFrom(qUser)
    .where(qUser.age.gt(ageMin)
           ,qUser.userName.startsWith("kim"))
    .fetch();

단순하게 위처럼 조건문을 작성하는 대신

BooleanBuilder 혹은 BooleanExpression 을 반환하는 함수를 사용하여, 가독성과 재사용성을 높일 수 있다.

public BooleanExpression ageMin(int ageMin){
    return QUserDTO.userDTO.age.gt(ageMin);
}
...
int ageMin = 20;
var result = query
		.selectFrom(qUser)
    .where(ageMin(ageMin)
           ,qUser.userName.startsWith("kim"))
    .fetch();
/* ------------------------------------------------------------------------- */
public BooleanExpression filterByAgeMinAndFamilyName(int ageMin, String fName){
    return QUserDTO.userDTO.userName.startsWith(fName)
        .and(ageMin(ageMin));
}
...
String fName ="lee";
ageMin = 10;
var result = query
		.selectFrom(qUser)
    .where(filterByAgeMinAndFamilyName(ageMin, fName))
    .fetch();

// 같은 엔티티 조인이나, 같은 엔티티의 서브쿼리일 경우, 따로 parameter로 Query 타입을 넘겨주는게 안전하다.
profile
좋은 개발자가 되고싶은

0개의 댓글