ETC
QueryDSL이란?
D_Helloper
2023. 6. 28. 22:27
QueryDSL란 ?
Query Domain Specific Language
SQL, JPQL 등을 코드로 작성할 수 있도록 해주는 빌더 오픈소스
💡 JPQL이란 ?
JPA에서 지원하는 다양한 쿼리 방법 중 가장 단순한 조회 방법으로, SQL의 경우에는 DB 테이블을 대상으로 쿼리를 질의하지만, JPQL은 엔티티 객체를 대상으로 쿼리를 질의
JPQL
String username = "java";
String jpql = "select m from Member m where m.username = :username";
List<Member> result = em.createQuery(query, Member.class).getResultList();
QueryDSL
String username = "java";
List<Member> result = queryFactory
.select(member)
.from(member)
.where(usernameEq(username))
.fetch();
QueryDSL의 장점
- 문자열이 아닌 코드로 작성하기 때문에 오타 및 에러를 쉽게 찾고 방지할 수 있음
- 동적인 쿼리 작성이 편리함
- 쿼리 작성 시 제약 조건 등을 메서드 추출을 통해 재사용할 수 있음
@Entity
public class Member {
@Id
@GeneratedValue
private Long id;
private String username;
private int age;
}
import com.querydsl.core.types.dsl.*;
public class QMember extends EntityPathBase<Member> {
public static final QMember member = new QMember("member");
public final NumberPath<Long> id = createNumber("id", Long.class);
public final StringPath username = createString("username");
public final NumberPath<Integer> age = createNumber("age", Integer.class);
// 생성자
public QMember(String variable) {
super(Member.class, variable);
}
}
QMember qMember = QMember.member;
JPAQueryFactory queryFactory = new JPAQueryFactory(entityManager);
List<Member> members = queryFactory.selectFrom(qMember)
.where(qMember.age.between(20, 30))
.fetch();
// Error: 컴파일 에러 발생
List<Member> members = queryFactory.selectFrom(qMember)
.where(qMember.age.eq("20")) // 잘못된 타입 사용
.fetch();
- 위 코드에서 qMember.age가 int형인 것을 컴파일러가 알고 있기 때문에 다른 타입의 값을 활용하려고 하면 컴파일 과정에서 에러 발생 → 타입 안정성 향상
💡 QClass란 ?
QueryDSL에서 도메인 모델 또는 엔티티 클래스와 관련된 쿼리 메타 모델 생성
- Domain 클래스의 쿼리 메타 모델을 생성
- 타입 안전성을 지원
- 코드 자동 완성 기능 활용 가능
- 동적 쿼리 작성
💡 JPAQueryFactory?
QueryDSL에서 JPA와 함께 사용하기 위한 클래스,
JPQLQuery를 생성하고 관리하는 데 사용되며, 주로 JPA의 entityManager와 함께 사용
사용 예시
Member Entity Class
@Entity
public class Member {
@Id
@GeneratedValue
private Long id;
private String username;
private int age;
// 생성자, getter, setter 등
}
QMember Class
import com.querydsl.core.types.dsl.*;
public class QMember extends EntityPathBase<Member> {
public static final QMember member = new QMember("member");
public final NumberPath<Long> id = createNumber("id", Long.class);
public final StringPath username = createString("username");
public final NumberPath<Integer> age = createNumber("age", Integer.class);
// 생성자
public QMember(String variable) {
super(Member.class, variable);
}
}
MemberRepository Interface
public interface MemberRepository extends JpaRepository<Member, Long>, MemberRepositoryCustom
{
}
MemberRepositoryCustom Interface
public interface MemberRepositoryCustom {
List<Member> findMembersByAgeBetween(int from, int to);
}
MemberRepositoryImple
import com.querydsl.jpa.impl.JPAQueryFactory;
public class MemberRepositoryImpl implements MemberRepositoryCustom {
private final JPAQueryFactory queryFactory;
public MemberRepositoryImpl(EntityManager em) {
this.queryFactory = new JPAQueryFactory(em);
}
@Override
public List<Member> findMembersByAgeBetween(int from, int to) {
QMember qMember = QMember.member;
return queryFactory.selectFrom(qMember)
.where(qMember.age.between(from, to))
.orderBy(qMember.age.asc())
.fetch();
}
}
MemberService
@Service
public class MemberService {
private final MemberRepository memberRepository;
public MemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
public List<Member> getMembersByAgeRange(int from, int to) {
return memberRepository.findMembersByAgeBetween(from, to);
}
}
CRUD 사용법
// SELECT 쿼리
queryFactory.selectFrom(qMember)
.where(qMember.age.between(20, 30))
.orderBy(qMember.username.asc())
.fetch();
// INSERT 쿼리
queryFactory.insert(qMember)
.set(qMember.username, "new-username")
.set(qMember.age, 25)
.execute();
// UPDATE 쿼리
queryFactory.update(qMember)
.set(qMember.username, "updated-username")
.where(qMember.id.eq(1L))
.execute();
// DELETE 쿼리
queryFactory.delete(qMember)
.where(qMember.id.eq(1L))
.execute();