JPQL中的LIMIT子句有什么替代方法?

127

我正在使用JPQL实现PostgreSQL查询。

这是一个可用的本地psql查询示例,

SELECT * FROM students ORDER BY id DESC LIMIT 1;

在JPQL中相同的查询无法工作。

@Query("SELECT s FROM Students s ORDER BY s.id DESC LIMIT 1")

Students getLastStudentDetails();

似乎在JPQL中LIMIT子句不起作用。

根据JPA文档,我们可以使用setMaxResults/setFirstResult,有人能告诉我如何在上述查询中使用它吗?


@NeilStockton 好的,我有点困惑,现在如何在我的上面的 JPQL 查询中使用 setMaxResults/setFirstResult? - Madhu
2
我使用了以下代码,它对我也起作用:@Query(value = "SELECT * FROM students ORDER BY id DESC LIMIT 1", nativeQuery = true) Object getLastStudentDetails(); - Madhu
1
不需要使用本地查询。请阅读链接的问答!!! - Stephen C
1
请参考参考指南或者查询方法,了解如何在Spring Data JPA中使用"order by date desc limit"。此外,该问题已经有一个重复的解决方案,请查看https://dev59.com/kZLea4cB1Zd3GeqP6L-5。 - M. Deinum
10个回答

143

您正在使用不支持限制结果的JPQL。在使用本机JPQL时,应该使用setMaxResults来限制结果。

但是,您正在使用Spring Data JPA,这使得限制结果变得非常容易。请参见参考指南中的此处,了解如何基于查询限制结果。在您的情况下,以下查找方法将完全符合您的要求。

findFirstByOrderById();

您也可以在查询中使用一个Pageable参数,而不是一个LIMIT子句。

@Query("SELECT s FROM Students s ORDER BY s.id DESC")
List<Students> getLastStudentDetails(Pageable pageable);

那么在您的调用代码中,可以按照以下方式执行(如参考指南中所述)。

getLastStudentDetails(PageRequest.of(0,1));

两者应该产生相同的结果,而不需要使用纯SQL语句。


2
新的PageRequest(0,1)对我有用,而不是new PageableRequest。我正在使用Spring 1.5.6。JPA 2.1有什么变化吗? - HopeKing
3
你的代码无法运行,原因是1)@Query中的LIMIT 1和2)getLastStudentDetails方法有一个参数类型为Pageable,但返回单个对象。这将在应用程序启动时失败(IllegalStateException: Method has to have one of the following return types! [interface org.springframework.data.domain.Slice, interface java.util.List, interface org.springframework.data.domain.Page])。 - Ilya Serbis
11
new PageRequest 已经被弃用。现在请使用 PageRequest.of(page, size) - Josh Stuart
1
使用 Pageable 而不是 LIMIT 子句的问题在于,Pageable 会导致查询的第二个 count(..) 版本也被执行。如果查询开销很大,这将导致查询时间增加一倍。 - Sanjiv Jivan
计数不应该太久,如果是的话,请检查您的查询和索引。 - M. Deinum
显示剩余2条评论

27

正如评论所述,JPQL不支持LIMIT关键字。

您可以使用setMaxResults来实现这一点,但如果您只想要一个单独的项目,则使用getSingleResult - 如果未找到任何项目,则会抛出异常。

因此,您的查询将类似于:

TypedQuery<Student> query = entityManager.createQuery("SELECT s FROM Students s ORDER BY s.id DESC", Student.class);    
query.setMaxResults(1);
如果您想设置特定的起始偏移量,请使用query.setFirstResult(initPosition)

3
嗨,我使用了这个代码,对我也起作用:@Query(value = "SELECT * FROM students ORDER BY id DESC LIMIT 1", nativeQuery = true) Object getLastStudentDetails();它会返回最后一位学生的详细信息。 - Madhu
2
如果您要使用本地查询,那么使用Hibernate就没有意义了。 - Siddharth Sachdeva
@SiddharthSachdeva 是的,没错。 - Madhu
7
不正确。如果你的应用程序有几十个实体和使用Hibernate或JPA执行几十个查询,在维护期间发现需要一次性转换为本地SQL,为什么不这样做呢? - idarwin
@SiddharthSachdeva,你应该始终选择最适合你需求的技术或方法 - 如果在特定情况下“Hibernate方式”不是最佳选择,我们不应该坚持使用它只因为它很酷。 - João Matos
@JoãoMatos 我宁愿在考虑安全性、性能问题和代码维护方面做出选择。如果这需要我学习技术,我也不介意。 - Siddharth Sachdeva

16

为了获取单行并在JPQL中使用LIMIT,我们可以告诉JPQL它是否为本地查询。

(使用 - nativeQuery=true)

下面是用法:

@Query("SELECT s FROM Students s ORDER BY s.id DESC LIMIT 1", nativeQuery=true)
Students getLastStudentDetails();

2
那是一个本地的SQL语句,不是JPQL。 - The Coder
1
这是一个nativeQuery,它不能工作 - 你需要将其编写为纯SQL。 "Select s FROM"不起作用,你需要使用"*"符号。 - Damian Wojakowski
如果我们权衡利弊,与使用 Pageable 相比,这种解决方案可能不太高效。 - Sham Fiorin
这个问题涉及到JPQL,而你的答案是带有错误语法的本地SQL。 - wirekang

9
正确的方式是编写您的JPA接口方法,如下所示。
public interface MyRepository extends PagingAndSortingRepository<EntityClass, KeyClass> {

List<EntityClass> findTop100ByOrderByLastModifiedDesc();
}

在方法名称中,“100”表示您想要获取的行数,否则您将把它们放在limit子句中。同时,“LastModified”是您想要按其排序的列。
PagingAndSortingRepository 或 CrudRepository,两者都可以使用。
为了完整起见,OP的接口方法应该是:
List<Students> findTop1ByIdDesc();

7

HQL中不能使用Limit,因为Limit是数据库特定的,所以Hibernate不允许通过HQL查询来使用它。

一个实现方式是使用子查询:

@Query("FROM Students st WHERE st.id = (SELECT max(s.id) FROM Students s)")
Students getLastStudentDetails();

这个限制是如何的? - undefined

4

JPQL不允许在由HQL生成的查询中添加limit关键字。您会收到以下异常。

org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected token: LIMIT near line 1

但是不用担心,有一种替代方法可以在由HQL生成的查询中使用limit关键字,只需按照以下步骤操作。

Sort.by(sortBy).descending() // 按降序获取记录

pageSize = 1 // 从降序结果集中获取第一条记录。

请参考以下服务类

服务:

@Autowired
StudentRepository repository; 

public List<Student> getLastStudentDetails(Integer pageNo, Integer pageSize, String sortBy)
{
    Integer pageNo = 0;
    Integer pageSize = 1;
    String sortBy = "id";
    Pageable paging = PageRequest.of(pageNo, pageSize, Sort.by(sortBy).descending());

    Slice<Student> pagedResult = repository.findLastStudent(paging);

    return pagedResult.getContent();
}

你的仓库接口应该实现 PagingAndSortingRepository

仓库:

public interface StudentRepository extends JpaRepository<Student,Long>, PagingAndSortingRepository<Student,Long>{

    @Query("select student from Student student")
    Slice<Student> findLastStudent(Pageable paging);
}

这将向您的查询中添加“limit”关键字,您可以在控制台中看到。希望这能有所帮助。


3

将分页(new PageRequest(0, 1))硬编码,以便只获取一条记录。

    @QueryHints({ @QueryHint(name = "org.hibernate.cacheable", value = "true") })
    @Query("select * from a_table order by a_table_column desc")
    List<String> getStringValue(Pageable pageable);

您需要传递 new PageRequest(0, 1) 来获取记录,然后从列表中获取第一条记录。


2

以下是一个有用的示例,列出了前十种服务。

代码片段中的REPOSITORY(存储库)将分数实体解析为ScoreTo(DTO类)的构造函数。

@Repository
public interface ScoreRepository extends JpaRepository<Scores, UUID> {     
  @Query("SELECT new com.example.parameters.model.to.ScoreTo(u.scoreId , u.level, u.userEmail, u.scoreLearningPoints, u.scoreExperiencePoints, u.scoreCommunityPoints, u.scoreTeamworkPoints, u.scoreCommunicationPoints, u.scoreTotalPoints) FROM Scores u "+
            "order by u.scoreTotalPoints desc")
    List<ScoreTo> findTopScore(Pageable pageable);
}

服务

@Service
public class ScoreService {
    @Autowired
    private ScoreRepository scoreRepository;    
  
    public List<ScoreTo> getTopScores(){
        return scoreRepository.findTopScore(PageRequest.of(0,10));
    }
}

1
你可以使用类似这样的代码:
 @Repository
 public interface ICustomerMasterRepository extends CrudRepository<CustomerMaster, String> 
 {
    @Query(value = "SELECT max(c.customer_id) FROM CustomerMaster c ")
    public String getMaxId();
 }

-1
由于您的查询很简单,您可以使用已接受答案的解决方案,并将您的查询命名为findFirstByOrderById(); 但是,如果您的查询更加复杂,我也发现了这种方法,无需使用本地查询:
@Query("SELECT MAX(s) FROM Students s ORDER BY s.id DESC")
Students getLastStudentDetails();

这里有一个实际的例子,其中无法使用命名查询方法。


对我来说失败了,因为无法在没有分组的情况下使用Max错误:列“loadtracki0_.load_tracking_status_id”必须出现在GROUP BY子句中或在聚合函数中使用 - Adam Hughes
我完全同意。 - Brian Lee
我完全同意。 - undefined
如果我完全理解了,当使用我的解决方案时你会得到一个错误吗? 你确定没有使用nativequery吗?我的解决方案在我的项目中与最新版本的Spring-Data JPA一起工作,唯一的区别是我像其他链接答案中查询了两个表。 - рüффп

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接