1. 멀티 모듈 프로젝트에서의 QueryDSL 설정
멀티 모듈 프로젝트에서 QueryDSL을 사용할 때는 다음과 같은 구조와 설정을 권장합니다:
my-project/
├── core/
│ └── build.gradle
├── api/
│ └── build.gradle
├── domain/
│ └── build.gradle
└── build.gradle
1.1 Root build.gradle 설정
buildscript {
ext {
queryDslVersion = "5.0.0"
}
}
subprojects {
apply plugin: 'java'
repositories {
mavenCentral()
}
}
1.2 Domain 모듈 build.gradle 설정
plugins {
id 'com.ewerk.gradle.plugins.querydsl' version '1.0.10'
}
dependencies {
implementation "com.querydsl:querydsl-jpa:${queryDslVersion}"
implementation "com.querydsl:querydsl-apt:${queryDslVersion}"
}
def querydslDir = "$buildDir/generated/querydsl"
querydsl {
jpa = true
querydslSourcesDir = querydslDir
}
sourceSets {
main.java.srcDir querydslDir
}
configurations {
querydsl.extendsFrom compileClasspath
}
compileQuerydsl {
options.annotationProcessorPath = configurations.querydsl
}
1.3 Entity 및 Repository 정의 (Domain 모듈)
ntity
public class User {
@Id @GeneratedValue
private Long id;
private String username;
// getters and setters
}
public interface UserRepository extends JpaRepository<User, Long>, QuerydslPredicateExecutor<User> {
}
1.4 API 모듈 설정
API 모듈의 build.gradle에 domain 모듈 의존성을 추가
dependencies {
implementation project(':domain')
}
2. 서비스 계층에서의 QueryDSL 사용
2.1 JPAQueryFactory 설정
@Service
public class UserService {
private final JPAQueryFactory queryFactory;
public UserService(EntityManager entityManager) {
this.queryFactory = new JPAQueryFactory(entityManager);
}
}
2.2 기본 쿼리 작성
public List<User> findUsersByUsernameAndAge(String username, int age) {
QUser user = QUser.user;
return queryFactory
.selectFrom(user)
.where(user.username.eq(username)
.and(user.age.gt(age)))
.fetch();
}
2.3 동적 쿼리 구성
public List<User> searchUsers(String username, Integer age) {
QUser user = QUser.user;
BooleanBuilder builder = new BooleanBuilder();
if (username != null) {
builder.and(user.username.contains(username));
}
if (age != null) {
builder.and(user.age.gt(age));
}
return queryFactory
.selectFrom(user)
.where(builder)
.fetch();
}
2.4 복잡한 쿼리 작성
public List<UserDTO> findUserWithPosts() {
QUser user = QUser.user;
QPost post = QPost.post;
return queryFactory
.select(Projections.constructor(UserDTO.class,
user.id,
user.username,
post.count()))
.from(user)
.leftJoin(user.posts, post)
.groupBy(user.id)
.fetch();
}
2.5 페이징 처리
public Page<User> findUsersPage(Pageable pageable) {
QUser user = QUser.user;
List<User> users = queryFactory
.selectFrom(user)
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.fetch();
long total = queryFactory
.selectFrom(user)
.fetchCount();
return new PageImpl<>(users, pageable, total);
}
3. 고급 페이징 처리 예시
다음은 복잡한 조건과 DTO 프로젝션을 포함한 페이징 처리 예시
Page<NoticeListResultDtoVo> noticeListResultDtoVo = applyPagination(
pageRequest,
contentQuery -> contentQuery
.select(new QPharmListResultDtoVo(
noticeRegistTarget.id,
noticeRegistTarget.title,
noticeRegistTarget.writer,
noticeRegistTarget.content,
noticeRegistTarget.file,
noticeRegistTarget.image,
noticeRegistTarget.createdDate,
))
.from(noticeRegistTarget)
.where(
noticeTitleEq(title),
writerEq(writer),
contentEq(content),
fileEq(file),
imageEq(image),
)
.orderBy(noticeRegistTarget.createdDate.desc()),
countQuery -> countQuery
.select(new QNoticeListResultDtoVo(
noticeRegistTarget.id,
noticeRegistTarget.title,
noticeRegistTarget.writer,
noticeRegistTarget.content,
noticeRegistTarget.file,
noticeRegistTarget.image,
noticeRegistTarget.createdDate
))
.from(noticeRegistTarget)
.where(
noticeTitleEq(title),
writerEq(writer),
contentEq(content),
fileEq(file),
imageEq(image)
)
);
이 예시에서 contentQuery와 countQuery는 다음과 같은 용도로 사용
- contentQuery: 실제 데이터를 조회하는 쿼리로 요청된 페이지에 해당하는 데이터를 조회함.
- countQuery: 전체 결과의 개수를 조회하는 쿼리로 페이징 처리에 필요한 전체 레코드 수를 계산함.
'데이터베이스 & ORM' 카테고리의 다른 글
VACUUM ANALYZE (1) | 2024.11.11 |
---|---|
PostgreSQL GIN (5) | 2024.11.10 |
JPA에서의 엔티티 참조 조회 방식 (1) | 2024.07.24 |
Q DTO (0) | 2024.07.21 |
Entity, Repository, DTO, domain, service의 연관성과 개념 (0) | 2024.07.20 |