전통적으로 JPQL, MyBatis, Criteria API 등은 쿼리를 문자열 형태로 작성해야했다. 이로 인해 오타나 구문 오류를 컴파일 시점에 잡아낼 수 없고, 런타임에서만 오류를 확인할 수 있었다. QueryDSL은 이러한 문제를 해결하기 위해 등장했으며, 타입 안전한 쿼리 작성을 통해 컴파일 시점에 오류를 검출하고 읽기 쉽게 유지보수하기 쉬운 코드를 작성할 수 있게 해준다.
APT(Annotation Processor Tool)
- 어노테이션이 있는 기존 코드를 바탕으로 새로운 파일들을 만들 수 있고, 이들을 이용한 클래스에서 컴파일하는 기능을 지원.
- Lombok의
@Getter나@Setter를 보면, APT가 컴파일 시점에 해당 어노테이션 기준으로 getter와 setter의 메서드를 만들어 주는 것.
String username = "java";
String jpql = "select m from Member m where m.username = :username";
List<Member> result = em.createQuery(jpql, Member.class)
.setParameter("username", username)
.getResultList();
String username = "java";
List<Member> result = queryFactory
.selectFrom(member)
.where(member.username.eq(username))
.fetch();
QueryDSL은 코드로 쿼리를 작성하여 오타가 발생할 확률이 적고 컴파일 시점에 오류를 검출할 수 있으며, 객체지향적으로 개발이 가능하다. 또한, IDE의 자동 완성 기능을 통해 더 쉽게 쿼리 작성이 가능하다.
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private int age;
// getters and setters
}
public List<User> searchUsers(String name, Integer minAge, Integer maxAge) {
JPAQueryFactory queryFactory = new JPAQueryFactory(entityManager);
QUser qUser = QUser.user;
BooleanBuilder builder = new BooleanBuilder();
if (name != null) {
builder.and(qUser.name.containsIgnoreCase(name));
}
if (minAge != null) {
builder.and(qUser.age.goe(minAge));
}
if (maxAge != null) {
builder.and(qUser.age.loe(maxAge));
}
return queryFactory.selectFrom(qUser)
.where(builder)
.fetch();
}
public BooleanExpression usernameEq(String username) {
return username == null ? null : QUser.user.username.eq(username);
}
public BooleanExpression ageBetween(Integer minAge, Integer maxAge) {
BooleanBuilder builder = new BooleanBuilder();
if (minAge != null) {
builder.and(QUser.user.age.goe(minAge));
}
if (maxAge != null) {
builder.and(QUser.user.age.loe(maxAge));
}
return builder;
}
public List<User> searchUsers(String username, Integer minAge, Integer maxAge) {
return queryFactory.selectFrom(QUser.user)
.where(usernameEq(username), ageBetween(minAge, maxAge))
.fetch();
}
이처럼 자주 사용하는 조건을 메서드로 추출하여 쿼리 작성 시 재사용할 수 있어 코드의 중복을 줄이고 유지보수성을 높일 수 있다.