[querydsl] Data JPA와 Querydsl 설정과 사용

2020. 9. 7. 14:35 Java 관련/etc ORM

버전과 환경은 아래와 같다.

  • Spring Boot:1.4
  • Spring Data JPA + Querydsl
  • Gradle
  • Intellij idea
  Querydsl을 실제 사용해본적이 없기 때문에 (코드는 언젠가 본 기억이 있다.) 프로젝트에 적용될 설정부터 해야했다. (역시 스프링은 설정이...)
  먼저 구글링을 했다. 설정 예제는 모두 대부분 maven 설정이였다. 나는 Gradle을 사용했기 때문에 Gradle 설정을 찾아봤다. 역시 나왔지만 삽질 끝에 얻어낸 한가지 문제(?)를 해결했다. (바보 같은 문제였지만, 구글링해서 나온 자료를 통해서 설정한다면 나처럼 또 누군가는 이런 실수를 할 거 같다는 생각에 정리한다.)

com.mysema.querydsl VS com.querydsl
  maven querydsl을 구글링해보자. 다른 패키지명으로된 querydsl이 검색되는데, 내가 구글링해서 찾은 Gradle 설정 예제는 모두 com.mysema.querydsl로 설정하라고 나왔다. 그렇게했더니 Data-JPA에서 의존하는 querydsl 버전과 내가 추가한(com.mysema.querydsl) querydsl dependency 버전이 맞지 않았다.
문제는 버전이였다. com.mysema.querydsl은 3.7.4가 마지막 버전이다. com.querydsl은 4.1.4 (현재날짜로) 가 마지막 버전이다. 즉 3.7.4 버전 이상부터 com.querydsl 패키지명으로 변경이 되었고, 최신버전을 사용한 Data-JPA는 com.querydsl에 의존하고 있던것이다. (참고)

gradle 설정
dependency 추가
1
2
3
4
5
6
7
8
9
10
11
dependencies {
    def querydslVersion = "4.1.4"
 
    /**
     * querydsl dependency
     */
    compile ("com.querydsl:querydsl-core:$querydslVersion")
    compile ("com.querydsl:querydsl-apt:$querydslVersion")
    compile ("com.querydsl:querydsl-jpa:$querydslVersion")
 
}
cs

Qclass generate task 추가
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
def queryDslOutput = file("src-gen/main/java")
 
sourceSets {
    main {
        java {
            srcDir "src-gen/main/java"
        }
    }
}
 
task generateQueryDSL(type: JavaCompile, group: 'build') {
    doFirst {
        if (!queryDslOutput.exists()) {
            logger.info("Creating `$queryDslOutput` directory")
 
            if (!queryDslOutput.mkdirs()) {
                throw new InvalidUserDataException("Unable to create `$queryDslOutput` directory")
            }
        }
    }
 
    source = sourceSets.main.java
    classpath = configurations.compile
    options.compilerArgs = [
            "-proc:only",
            "-processor",
            "com.querydsl.apt.jpa.JPAAnnotationProcessor"
    ]
    destinationDir = queryDslOutput
}
 
compileTestJava.dependsOn(generateQueryDSL)
cs
  21라인을 보면 패키지명이 com.querydsl.apt.jpa로 된것이 보인다. 앞서 설명했듯이 com.mysema.query.apt로 시작한다. (주의하자.)

springBoot Main Class 설정
1
2
3
springBoot {
    mainClass = "net.woniper.querydsl.SpringBootQuerydslApplication"
}
cs
  QClass를 생성하기 위해 bootRepackage task를 실행할 것이다. 때문에 위와같이 springBoot Main Class 경로가 설정되어 있어야한다.

clean task 시 QClass 제거
1
2
3
4
5
6
/**
 * gradle clean task 실행 시 querydsl 경로 제거
 */
clean {
    delete queryDslOutput
}
cs

  querydsl에서는 QClass라는 파일이 생성되어야 한다. gradle clean task 실행 시 QClass를 모두 삭제한다. 전체 소스는 github에 있다. 참고하자. (제일 하단에 링크 참고)


JPA Entity

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
package net.woniper.querydsl.domain;
 
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
 
@Entity
public class Foo {
 
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "foo_id")
    private Long id;
 
    @Column(name = "bar")
    private String bar;
 
    public Foo() {}
 
    public Foo(String bar) {
        setBar(bar);
    }
 
    public Long getId() {
        return id;
    }
 
    public Foo setId(Long id) {
        this.id = id;
        return this;
    }
 
    public String getBar() {
        return bar;
    }
 
    public Foo setBar(String bar) {
        this.bar = bar;
        return this;
    }
 
    @Override
    public String toString() {
        return "Foo{" +
                "id=" + id +
                ", bar='" + bar + '\'' +
                '}';
    }
 
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Foo)) return false;
 
        Foo foo = (Foo) o;
 
        if (getId() != null ? !getId().equals(foo.getId()) : foo.getId() != nullreturn false;
        return getBar() != null ? getBar().equals(foo.getBar()) : foo.getBar() == null;
 
    }
 
    @Override
    public int hashCode() {
        int result = getId() != null ? getId().hashCode() : 0;
        result = 31 * result + (getBar() != null ? getBar().hashCode() : 0);
        return result;
    }
}
 
cs

 JPA나 Entity Class를 따로 설명하지 않겠다. querydsl에서 생성되는 QClass는 바로 이 JPA Entity를 기준으로 QClass를 생성한다. 앞서 gradle 설정이 바로 이 Entity를 기준으로 QClass를 생성하기 위한 설정이다.


Repository

1
2
3
4
5
6
7
8
9
10
11
package net.woniper.querydsl.repository;
 
import net.woniper.querydsl.domain.Foo;
 
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.querydsl.QueryDslPredicateExecutor;
 
public interface FooRepository extends JpaRepository<Foo, Long>, QueryDslPredicateExecutor<Foo> {
    Foo findByBar(String bar);
}
 
cs

  8라인을 보면 JpaRepository QueryDslPredicateExecutor를 상속받았다. JPA에서 Repository 선언은 JpaRepository 상속으로 충분하지만, querydsl을 사용하려면 QueryDslPredicateExecutor 또한 상속이 필요하다.


QClass 생성

  기본 설정은 끝났다. QClass가 정상적으로 생성되는지 확인해보자. 앞서 설명했던 gradle bootRepackage 명령어를 실행시켜 보자. 생성 위치인 "src/main/generated" 경로에 QFoo Class가 생성된다면 정상적으로 설정된것이다. (gradle clean 명령어도 실행시켜보자. "src/main/generated" 경로가 삭제되어야한다.


Test

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
package net.woniper.querydsl;
 
import com.querydsl.core.BooleanBuilder;
 
import net.woniper.querydsl.domain.Foo;
import net.woniper.querydsl.domain.QFoo;
import net.woniper.querydsl.repository.FooRepository;
 
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
 
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
 
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringBootQuerydslApplicationTests {
 
    @Autowired
    private FooRepository repository;
 
    @Before
    public void setUp() throws Exception {
        repository.save(new Foo("foo1"));
        repository.save(new Foo("foo2"));
        repository.save(new Foo("foo3"));
        repository.save(new Foo("bar1"));
        repository.save(new Foo("bar2"));
        repository.save(new Foo("bar3"));
    }
 
    @Test
    public void testFoo() {
        Foo foo1 = repository.findByBar("foo1");
        assertThat(foo1.getBar(), is("foo1"));
    }
 
    @Test
    public void testQuerydslFindLikeBar() throws Exception {
        // given
        String likeWhere = "bar%";
        QFoo foo = QFoo.foo;
        BooleanBuilder builder = new BooleanBuilder().and(foo.bar.like(likeWhere));
 
        // when
        Iterable<Foo> foos = repository.findAll(builder);
 
        // then
        foos.forEach(System.out::println);
        foos.forEach(f -> assertTrue(f.getBar().startsWith("bar")));
    }
}
 
cs

테스트를 돌려보자. 


마무리

  프로젝트 설정과 코드를 세세한 설명이 부족하다는것은 알지만, 이 포스팅은 Data-JPA + querydsl + intellij 상황에서 어떻게 설정해야되는지 삽질 결과를 공유하기 위함이다. 상세 설정과 코드는 https://github.com/woniper/spring-example/tree/master/spring-boot-querydsl 여기를 참고하자.

출처 : https://blog.woniper.net/317?category=531455