Querydsl 동적 페이지네이션 쿼리
Querydsl로 조건에 따라 where 절이 달라지는 커서 기반 페이지네이션 동적 쿼리를 작성하는 방법을 JpaRepository 방식과 비교해 예제 코드로 설명합니다.
Spring에서 Querydsl로 동적 커서 기반 페이지네이션 쿼리를 구성하는 방법에 대해 알아보겠습니다.
개요
동적 쿼리란?
동적 쿼리는 상황에 알맞는 쿼리문을 생성해서 데이터베이스에 날려주는것 입니다.
예시 1
users 테이블에서 커서 기반 페이지네이션으로 회원을 조회하고 싶으면 다음과 같이 코드를 구현해야 됩니다.
JpaRepository
1
2
3
public interface UsersRepository extends JpaRepository<Long, Users> {
Page<Users> findAll(Pageable pageable);
}
Querydsl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@RequiredArgsConstructor
public class UserRepositoryImpl implements UserCustomRepository {
private final JPAQueryFactory queryFactory;
@Override
public Page<Users> findByPage(long pageFrom, int pageSize) {
Long count = queryFactory.select(user.count())
.from(user)
.where(
user.isSeekingTeam.isTrue()
).fetchOne();
Pageable pageable = Pageable.ofSize(pageSize);
if (count == null || count == 0)
return new PageImpl<>(List.of(), pageable, 0);
List<User> users = queryFactory.selectFrom(user)
.where(
user.id.lt(pageFrom),
user.isSeekingTeam.isTrue()
).orderBy(user.createdAt.desc())
.limit(pageSize)
.fetch();
return new PageImpl<>(users, pageable, count);
}
}
SQL
1
SELECT * FROM users WHERE user_id<100 ORDER BY created_at DESC LIMIT 10;
예시 2
동일한 조건에서 position이 BACKEND인 회원으로 조건을 추가하고 싶으면 다음과 같이 코드를 구현해야 됩니다.
JpaRepository
1
2
3
public interface UsersRepository extends JpaRepository<Long, Users> {
Page<Users> findAllByPosition(Pageable pageable, Position position);
}
Querydsl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@RequiredArgsConstructor
public class UserRepositoryImpl implements UserCustomRepository {
private final JPAQueryFactory queryFactory;
@Override
public Page<Users> findByPage(Position position, long pageFrom, int pageSize) {
Long count = queryFactory.select(user.count())
.from(user)
.where(
user.position.eq(position)
).fetchOne();
Pageable pageable = Pageable.ofSize(pageSize);
if (count == null || count == 0)
return new PageImpl<>(List.of(), pageable, 0);
List<User> users = queryFactory.selectFrom(user)
.where(
user.id.lt(pageFrom),
user.position.eq(position)
).orderBy(user.createdAt.desc())
.limit(pageSize)
.fetch();
return new PageImpl<>(users, pageable, count);
}
}
SQL
1
SELECT * FROM users WHERE user_id<100 AND position='BACKEND' ORDER BY created_at DESC LIMIT 10;
동적 쿼리 구현
예시1도 필요하고 예시2도 필요한 쿼리라고 가정을 하겠습니다. 정적 쿼리만 활용한다면 예시1과 예시2의 코드를 둘 다 작성해야 됩니다.
하지만 동적 쿼리를 사용하면 두개의 코드를 하나로 묶어서 효율적이고 간결하게 코드를 구성할 수 있습니다.
동적 쿼리를 활용하기 위해서는 Jpa, Jdbc 또는 Querydsl 등을 활용할 수 있는데, JpaRepository interface로는 활용할 수 없습니다. 이 중 컴파일 과정에서 에러를 발견할 수 있는 Querydsl로 동적 쿼리를 작성해 보겠습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
@RequiredArgsConstructor
public class UserRepositoryImpl implements UserCustomRepository {
private final JPAQueryFactory queryFactory;
@Override
public Page<User> findPage(Position position, long pageFrom, int pageSize) {
Long count = queryFactory.select(user.count())
.from(user)
.where(
positionEq(position),
).fetchOne();
Pageable pageable = Pageable.ofSize(pageSize);
if (count == null || count == 0)
return new PageImpl<>(List.of(), pageable, 0);
List<User> users = queryFactory.selectFrom(user)
.where(
user.id.lt(pageFrom),
positionEq(position),
).orderBy(user.createdAt.desc())
.limit(pageSize)
.fetch();
return new PageImpl<>(users, pageable, count);
}
private Predicate positionEq(Position position) {
return position != Position.NONE ? user.position.eq(position) : null;
}
}
위의 코드를 보면 positionEq()메서드에서 만약 Position.NONE일 경우에 where문에 null을 반환하고, 반대의 경우에는 user.position.eq(position)을 반환하게 되어있습니다. Querydsl은 where문에 null이 들어오면 무시를 하고 쿼리문을 구성할 수 있기에 가능한 것입니다.