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); } ... }
이렇게 한다면 디비에는 다음과 같이 값이 들어갈 것이다.
'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 |