QueryDSL 시작하기

By | 2022년 7월 22일
Table of Content

QueryDSL 시작하기

MariaDB 설치

여기 에서 MariaDB 를 다운받아 설치한다.

JPA 테스트

CREATE DATABASE db_test;

USE db_test;

SHOW TABLES;

CREATE TABLE tbl_user (
    id int NOT NULL AUTO_INCREMENT,
    username varchar(32),
    email varchar(32),
    PRIMARY KEY (id)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB;

build.gradle

......
dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    implementation 'org.mariadb.jdbc:mariadb-java-client:3.0.5'
    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
......

application.yml

spring:
  datasource:
    driverClassName: org.mariadb.jdbc.Driver
    url: jdbc:mariadb://localhost:3306/db_test
#    driverClassName: com.mysql.cj.jdbc.Driver
#    url: jdbc:mysql://localhost:3306/test
    username: testuser
    password: testpass
  jpa:
    show-sql: true
    hibernate:
      ddl-auto: update
      format_sql: true
@Getter
@Builder
@Entity
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "tbl_user", catalog = "db_test")
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    private String username;

    private String email;
}
public interface UserRepository extends JpaRepository<User, Integer> {
    List<User> findByUsername(String username);
}
@SpringBootTest
class UserRepositoryTest {

    @Autowired
    private UserRepository repository;

    @Test
    void jpa() {
        // given
        User user = User
                .builder()
                .username("Lee")
                .email("skyer9@gmail.com")
                .build();

        // when
        repository.save(user);
        List<User> list = repository.findByUsername("Lee");

        // then
        assertTrue(list.size() > 0);

        User item = list.get(0);
        Assertions.assertEquals(item.getUsername(), "Lee");
    }
}

QueryDSL 설정

// 1. queryDsl version
buildscript {
    ext {
        queryDslVersion = "5.0.0"
    }
}

plugins {
    id 'org.springframework.boot' version '2.6.3'
    id 'io.spring.dependency-management' version '1.0.11.RELEASE'
    // 2. querydsl plugins
    id "com.ewerk.gradle.plugins.querydsl" version "1.0.10"
    id 'java'
}

//...

dependencies {
    // 3. querydsl dependencies
    implementation "com.querydsl:querydsl-jpa:${queryDslVersion}"
    implementation "com.querydsl:querydsl-apt:${queryDslVersion}"
    //...
}

test {
    useJUnitPlatform()
}

/*
 * queryDSL 설정 추가
 */
// querydsl에서 사용할 경로 설정
def querydslDir = "$buildDir/generated/querydsl"
// JPA 사용 여부와 사용할 경로를 설정
querydsl {
    jpa = true
    querydslSourcesDir = querydslDir
}
// build 시 사용할 sourceSet 추가
sourceSets {
    main.java.srcDir querydslDir
}
// querydsl 컴파일시 사용할 옵션 설정
compileQuerydsl{
    options.annotationProcessorPath = configurations.querydsl
}
// querydsl 이 compileClassPath 를 상속하도록 설정
configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
    querydsl.extendsFrom compileClasspath
}

아래와 같이 compileQuerydsl 을 실행시키면,
QClass 가 생성된다.

QueryDSL 테스트

@Configuration
public class QueryDslConfig {

    @PersistenceContext
    private EntityManager entityManager;

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

ompileQuerydsl 를 먼저 실행시켜야 한다.

annotation 활성화를 시켜줘야 한다.
Lombok 이 작동한다고 annotation 이 활성화되어 있다고 생각하지 말자.
여전히 인식하지 못하면 프로젝트를 닫았다 다시 열자.

import static com.example.querydsltest.domain.QUser.user;

@Repository
public class UserDslRepository extends QuerydslRepositorySupport {

    private final JPAQueryFactory jpaQueryFactory;

    public UserDslRepository(JPAQueryFactory jpaQueryFactory) {
        super(User.class);
        this.jpaQueryFactory = jpaQueryFactory;
    }

    public List<User> list() {
        return jpaQueryFactory
                .selectFrom(user)
                .where(user.username.eq("Lee")).fetch();
    }
}
@SpringBootTest
class UserDslRepositoryTest {

    @Autowired
    UserDslRepository repository;

    @Test
    void querydsl() {
        // given

        // when
        List<User> list = repository.list();

        // then
        assertTrue(list.size() > 0);

        User item = list.get(0);
        Assertions.assertEquals(item.getUsername(), "Lee");
    }
}

group by

생성자는 쓰이지 않고 Setter 가 쓰인다.
따라서, @Setter@NoArgsConstructor 는 반드시 있어야 한다.

생성자를 이용한 객체 생성의 경우,
누군가 생성자 파라미터의 순서가 맘에 들지 않는다고,
뜯어고쳤을 때 찾아낼 수 없는 버그가 만들어지기에 좋지않다.

@Getter
@Setter
@NoArgsConstructor
public class UserGroupByDto {

    private String username;
    private Long count;
}
    public List<UserGroupByDto> groupBy() {

        return jpaQueryFactory
                .select(
                        Projections.bean(
                                UserGroupByDto.class
                                , user.username
                                , user.count().as("count")
                        )
                )
                .from(user)
                .groupBy(user.username)
                .orderBy(user.username.asc())
                .fetch();
    }
    @Test
    void groupBy() {
        // given

        // when
        List<UserGroupByDto> list = repository.groupBy();

        // then
        assertTrue(list.size() > 0);

        UserGroupByDto item = list.get(0);
        Assertions.assertEquals(item.getUsername(), "Lee");
        Assertions.assertEquals(item.getCount(), 3);
    }

JPA repository 에서 QueryDSL 사용하기

DSL Repository 인터페이스를 생성합니다.

public interface UserDslRepository {

    List<User> list();
}

UserRepository 에 추가해 줍니다.

public interface UserRepository extends JpaRepository<User, Integer>, UserDslRepository {
    List<User> findByUsername(String username);
}

구현 클래스는 클래스명이 DSL Repository 인터페이스 + Impl 로 작성해야 합니다.

public class UserDslRepositoryImpl extends QuerydslRepositorySupport implements UserDslRepository {

    private final JPAQueryFactory jpaQueryFactory;

    public UserDslRepositoryImpl(JPAQueryFactory jpaQueryFactory) {
        super(User.class);
        this.jpaQueryFactory = jpaQueryFactory;
    }

    @Override
    public List<User> list() {
        System.out.println("DSL Called");
        return jpaQueryFactory
                .selectFrom(user)
                .where(user.username.eq("Lee")).fetch();
    }
}

JPA Repository 에서 DSL 이 호출되는 것을 확인할 수 있습니다.

@SpringBootTest
class UserRepositoryTest {

    @Autowired
    private UserRepository repository;

    @Test
    void listWithDsl() {
        // given

        // when
        List<User> list = repository.list();

        // then
        assertTrue(list.size() > 0);

        User item = list.get(0);
        Assertions.assertEquals(item.getUsername(), "Lee");
    }
}
```

```java

답글 남기기