A LITTLE HACK FOR CURSOR-BASED PAGINATION
Cursor-based pagination can have benefits over offset-based pagination especially when the number of records is huge. If the database has a lot of records, the database will have to skip the offset number of records before returning the result. On the otherhand, if the database is properly indexed, cursor-based pagination can be faster as it can directly jump to the record based on the cursor.
As in any pagination methods, you need to return the next cursor to fetch the next page of records.
A neat trick here is to fetch one (or more) records more than the page size to check if there is a next page.
Here is a simple trick to handle cursor-based pagination in querydsl
syntax:
import com.querydsl.jpa.impl.JPAQueryFactory
data class Paginated<T>(
val data: List<T>,
val next: Long?
)
@Repository
class UserRepository(
private val entityManager: EntityManager
) {
private val queryFactory = JPAQueryFactory(entityManager)
fun findUsers(pageSize: Int, next: Long?): Paginated<User> {
val query = queryFactory
.selectFrom(QUser.user)
.orderBy(QUser.user.name.desc())
next?.let {
queryFactory
.selectFrom(QUser.user)
.where(QUser.user.id.eq(it))
.fetchOne()?.let { cursor ->
query.where(QUser.user.name.lt(cursor.name))
}
}
// #1. fetch just 1 more record to check if there is a next page
val users = query
.limit((pageSize + 1).toLong())
.fetch()
// #2. check next cursor by checking the size of fetched records
val nextCursor = if (users.size > pageSize) users[pageSize - 1].id else null
return Paginated(users.take(pageSize), nextCursor?.id)
}
}