QueryDsl이란?

  • 오픈소스 프로젝트다.
  • 일반적으로 복잡한 Creteria를 대체하는 JPQL 빌더다.
  • JPA의 표준스펙이 아니므로 약간의 설정이 더 필요하다.
  • 복잡한 쿼리와 동적쿼리를 깔쌈하게 해결해준다.
  • 쿼리를 자바 코드로 작성할 수 있다. 따라서 문법오류를 컴파일단계에서 잡아줄 수 있다.

JPQL vs QueryDSL

JPQL

String username = "gracelove"
String query = "select m from Member m"+
 "where m.username = :username";

List<Member> result = em.createQuery(query, Member.class)
                            .getResult.List();
  • 이 코드에 문제가 있다. 쉽게 발견할 수 있는가?
    • String query에 가독성을 위해 개행을 줬다. 이걸 한 줄로 풀어보자.
    • select m from Member mwhere m.username = :username
    • 한 줄로 풀어보니, "...m where..."가 아니라 "...mwhere..."다.
    • 컴파일러가 잡아주지도 않는다. 런타임때 이 쿼리를 실행시키면 그제서야 에러가 난다.

QueryDSL

String username = "gracelove
List<Member> result = queryFactory
                            .select(member)
                            .from(member)
                            .where(member.username.eq(username))
                            .fetch();
  • 자바 코드기 때문에 ide의 도움을 받을 수 있다. 또 컴파일 단계에서 오류를 발견할 수 있다. 게다가 자바 코드다 보니, 메서드추출도 할 수 있어서 재사용성 또한 높아진다.

maven에서의 설정방법

pom.xml 의존성 추가

    <dependencies>
        <dependency>
            <groupId>com.querydsl</groupId>
            <artifactId>querydsl-apt</artifactId>
        </dependency>
        <dependency>
            <groupId>com.querydsl</groupId>
            <artifactId>querydsl-jpa</artifactId>
        </dependency>
    </dependencies>
  • 의존성을 위와 같이 추가한다.

pom.xml 빌드 플러그인 추가

<build>
  <plugins>
        <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>
    </plugins>
</build>
  • 이렇게 설정한뒤 mvn compile 을 실행시키자. 그럼 @Entity를 붙인 클래스들이 Q타입으로 새롭게 생성된다. ex) Account -> QAccount
    • target 디렉토리 밑에 Q타입의 class가 생성된 걸 볼 수 있다.

사용하기.

  • 먼저 Entity 클래스들을 만들어보자.
  • Member 클래스와 Team 클래스를 만들 것이다.
  • 연관관계는 다 대 일이다. 양방향으로 건다. 연관관계의 주인 즉, fk를 갖고있는 건 '다' 쪽인 Member 클래스다.

Member.class

/**
 * Created by GraceLove
 * Github  : https://github.com/gracelove91
 * Blog    : https://gracelove91.tistory.com
 * Email   : govlmo91@gmail.com
 *
 * @author : Eunmo Hong
 * @since : 2020/06/07
 */

@ToString(of = {"id", "username", "age"})
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
@Getter
public class Member {

    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "member_id")
    private Long id;
    private String username;
    private int age;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "team_id")
    private Team team;


    public Member(String username) {
        this(username, 0, null);
    }


    public Member(String username, int age) {
        this(username, age, null);
    }

    public Member(String username, int age, Team team) {
        this.username = username;
        this.age = age;
        if (team != null) {
            changeTeam(team);
        }
    }

    public void changeTeam(Team team) {
        this.team = team;
        team.getMembers().add(this);
    }
}

Team.class

/**
 * Created by GraceLove
 * Github  : https://github.com/gracelove91
 * Blog    : https://gracelove91.tistory.com
 * Email   : govlmo91@gmail.com
 *
 * @author : Eunmo Hong
 * @since : 2020/06/07
 */

@ToString(of = {"id", "name"})
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
@Getter
public class Team {

    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "team_id")
    private Long id;
    private String name;

    @OneToMany(mappedBy = "team")
    private List<Member> members = new ArrayList<>();

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

사용 코드

  • 먼저 EntityManager와 JPAQueryFactory를 필드로 두고, EntityManager는 빈을 주입받자.

  • 그 다음 JPAQueryFactory의 인스턴스를 생성하는데, 생성자 인자로 EntityManager를 주자.

    • JPAQueryFactory는 스레드 세이프 하기 때문에 빈으로 등록시켜서 싱글톤으로 이용해도 괜찮다.
  • 테스트를 해볼 데이터를 @BeforeEach 로 각 테스트 이전에 넣어준다.

    @SpringBootTest
    @Transactional
    class MemberTest {
    
      @PersistenceContext //@Autowired
      EntityManager em;
      JPAQueryFactory queryFactory;
    
      @BeforeEach
      public void testEntity() {
          queryFactory = new JPAQueryFactory(em);
    
          Team teamA = new Team("teamA");
          Team teamB = new Team("teamB");
          em.persist(teamA);
          em.persist(teamB);
    
          Member member1 = new Member("member1", 10, teamA);
          Member member2 = new Member("member2", 20, teamA);
    
          Member member3 = new Member("member3", 30, teamB);
          Member member4 = new Member("member4", 40, teamB);
          em.persist(member1);
          em.persist(member2);
          em.persist(member3);
          em.persist(member4);
      }
      ...
    }
  • 이렇게 한다면 디비에는 다음과 같이 값이 들어갈 것이다.

전체 소스 : https://github.com/gracelove91/querydsl-practice/blob/master/src/test/java/kr/gracelove/querydsl/entity/MemberTest.java

'JAVA > JPA' 카테고리의 다른 글

[JPA]open-session-in-view 를 알아보자  (1) 2020.07.01
@EntityGraph  (0) 2020.02.11
순수 JPA레포지토리의 페이징과 스프링 데이터 JPA레포지토리의 페이징.  (0) 2020.02.10
연관관계의 주인  (0) 2020.01.27
준영속 상태  (0) 2020.01.16

+ Recent posts