NEVER USE QUERYDSL PREDICATE EXECUTOR

I personally like type-safe queries other than ORMs, and querydsl is a perfect middle ground between raw SQL queries and ORM. Spring Data JPA already provides QueryDslPredicateExecutor to execute queries based on Predicate instances from querydsl, but it lacks core functionalities such as joins, ordering, and limits.

@Repository
interface UserRepository : JpaRepository<User, Long>, QueryDslPredicateExecutor<User>

Let's say we want to find a page of users ordered by name in descending order. The findAll method only take predicate and a Pageable instance, which is not enough to create a query with ordering:

import org.springframework.data.querydsl.QuerydslPredicateExecutor
 
@Service
class UserService(
    private val userRepository: UserRepository
) {
    fun findUsers(pageable: Pageable): Page<User> {
        // no ordering!
        val predicate = QUser.user.name.isNotNull
        return userRepository.findAll(predicate, pageable)
    }
}

Instead of using QueryDslPredicateExecutor, you can use Querydsl directly to create type-safe queries.

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))
            }
        }
        
        return Paginated(
          items=query.limit((pageSize + 1).toLong()).fetch().take(pageSize),
          next=items.get(pageSize - 1)?.id
        )
    }
}