1. 概述
在本教程中,我们将学习使用JPA和Spring Data JPA分页查询结果。
首先,我们看一下我们要查询的表,以及我们要实现的SQL查询。
然后我们将深入探讨如何使用JPA和Spring Data JPA实现这一目标。
2. 测试数据
下面是我们将在整篇文章中查询的表。
我们要回答的问题是,“第一个被占用的座位是什么,谁在占用它?
First Name | Last Name | Seat Number |
---|---|---|
Jill | Smith | 50 |
Eve | Jackson | 94 |
Fred | Bloggs | 22 |
Ricki | Bobbie | 36 |
Siya | Kolisi | 85 |
3. SQL
使用SQL,我们可能会编写如下所示的查询:
SELECT firstName, lastName, seatNumber
FROM passengers
ORDER BY seatNumber LIMIT 1;
4. JPA构建
使用JPA,我们首先需要一个实体来映射我们的表:
@Entity
class Passenger {
@Id
@GeneratedValue
@Column(nullable = false)
private Long id;
@Basic(optional = false)
@Column(nullable = false)
private String firstName;
@Basic(optional = false)
@Column(nullable = false)
private String lastName;
@Basic(optional = false)
@Column(nullable = false)
private Integer seatNumber;
// constructor, getters ...
}
接下来我们需要一个封装查询代码的方法,在这里实现为PassengerRepositoryImpl.findOrderedBySeatNumberLimitedTo(int limit):
@Repository
class PassengerRepositoryImpl implements CustomPassengerRepository {
@PersistenceContext
private EntityManager entityManager;
@Override
public List<Passenger> findOrderedBySeatNumberLimitedTo(int limit) {
return entityManager.createQuery("select p from Passenger p order by p.seatNumber", Passenger.class)
.setMaxResults(limit).getResultList();
}
}
在我们的Repository方法中,我们使用EntityManager创建一个Query,在这个Query上我们调用setMaxResults()方法。
对Query.setMaxResults()的调用最终会导致将limit语句附加到生成的SQL中:
select passenger0_.id as id1_15_,
passenger0_.fist_name as fist_nam2_15_,
passenger0_.last_name as last_nam3_15_,
passenger0_.seat_number as seat_num4_15_
from passenger passenger0_
order by passenger0_.seat_number
limit ?
5. Spring Data JPA
我们还可以使用Spring Data JPA生成我们的SQL。
5.1 first或者top
我们可以解决这个问题的一种方法是使用带有关键字first或top的方法名称派生。
我们可以选择指定一个数字作为将返回的最大结果大小。如果我们省略它,Spring Data JPA假设结果大小为1。
由于我们想知道哪个是第一个被占用的座位以及谁正在占用它,我们可以通过以下两种方式将其省略:
interface PassengerRepository extends JpaRepository<Passenger, Long>, CustomPassengerRepository {
Passenger findFirstByOrderBySeatNumberAsc();
Passenger findTopByOrderBySeatNumberAsc();
}
如果我们限制为一个实例结果,如上所述,那么我们也可以使用Optional包装结果:
Optional<Passenger> findFirstByOrderBySeatNumberAsc();
Optional<Passenger> findTopByOrderBySeatNumberAsc();
5.2 Pageable
或者,我们可以使用Pageable对象:
Page<Passenger> page = repository.findAll(PageRequest.of(0, 1, Sort.by(Sort.Direction.ASC, "seatNumber")));
如果我们看一下JpaRepository的默认实现SimpleJpaRepository类,我们可以看到它也调用了Query.setMaxResults():
public class SimpleJpaRepository<T, ID> implements JpaRepositoryImplementation<T, ID> {
protected <S extends T> Page<S> readPage(TypedQuery<S> query,
Class<S> domainClass, Pageable pageable,
@Nullable Specification<S> spec) {
if (pageable.isPaged()) {
query.setFirstResult((int) pageable.getOffset());
query.setMaxResults(pageable.getPageSize());
}
return PageableExecutionUtils.getPage(query.getResultList(), pageable,
() -> executeCountQuery(this.getCountQuery(spec, domainClass)));
}
}
5.3 比较
这两种选择都会生成我们所期望的SQL,first和top偏于约定,而Pageable偏于配置:
select passenger0_.id as id1_15_,
passenger0_.fist_name as fist_nam2_15_,
passenger0_.last_name as last_nam3_15_,
passenger0_.seat_number as seat_num4_15_
from passenger passenger0_
order by passenger0_.seat_number asc
limit ?
6. 总结
在JPA中限制查询结果与SQL略有不同;我们不会将limit关键字直接包含在我们的JPQL中。
相反,我们只需对Query#maxResults进行单个方法调用,或者在Spring Data JPA方法名称中使用关键字first或top。
与往常一样,本教程的完整源代码可在GitHub上获得。