123

I'm working with PostgreSQL query implementing in JPQL.

This is a sample native psql query which works fine,

SELECT * FROM students ORDER BY id DESC LIMIT 1;

The same query in JPQL doesnt work,

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

Students getLastStudentDetails();

seems like LIMIT clause doesn't work in JPQL.

According to JPA documentation we can use setMaxResults/setFirstResult, Can anyone tell me how can I use that in my above query?

tk_
  • 16,415
  • 8
  • 80
  • 90
Madhu
  • 2,643
  • 6
  • 19
  • 34
  • @NeilStockton okay, I was bit confused about it, now how to use setMaxResults/setFirstResult in my above jpql query? – Madhu Jun 15 '17 at 11:25
  • 2
    I used this which also worked for me `@Query(value = "SELECT * FROM students ORDER BY id DESC LIMIT 1", nativeQuery = true) Object getLastStudentDetails();` – Madhu Jun 15 '17 at 11:45
  • 1
    No. You don't have to use a native query. Read the linked Q&A !!! – Stephen C Jun 15 '17 at 11:48
  • 1
    Duplicate of https://stackoverflow.com/questions/34640488/order-by-date-desc-limit-in-spring-data-jpa and also check the [reference guide](http://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.special-parameters) or how to write [query methods](http://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.limit-query-result). – M. Deinum Jun 15 '17 at 12:02

10 Answers10

138

You are using JPQL which doesn't support limiting results like this. When using native JPQL you should use setMaxResults to limit the results.

However you are using Spring Data JPA which basically makes it pretty easy to do. See here in the reference guide on how to limit results based on a query. In your case the following, find method would do exactly what you want.

findFirstByOrderById();

You could also use a Pageable argument with your query instead of a LIMIT clause.

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

Then in your calling code do something like this (as explained here in the reference guide).

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

Both should yield the same result, without needing to resort to plain SQL.

Nourdine Alouane
  • 804
  • 12
  • 22
M. Deinum
  • 115,695
  • 22
  • 220
  • 224
  • 1
    new PageRequest(0,1) worked for me instead of new PageableRequest. I am using Spring 1.5.6. Did something change in JPA 2.1 ? – HopeKing Jan 27 '18 at 06:04
  • 3
    Your code doesn't work because 1) `LIMIT 1` in `@Query` and 2) `getLastStudentDetails` has parameter of type `Pageable` but returns single object. This will fail on app start (`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 Mar 23 '18 at 11:23
  • 11
    `new PageRequest` has been deprecated. Now use `PageRequest.of(page, size)` – Josh Stuart Dec 26 '18 at 07:24
  • 1
    The problem with using Pageable instead of a LIMIT clause is that Pageable causes a second count(..) version of the query to be also executed and if the query is expensive, this results in it taking twice the amount of time, – Sanjiv Jivan Mar 12 '21 at 20:41
  • A count shouldn't take too long, if it does check your query and your indexes. – M. Deinum Mar 15 '21 at 06:45
  • The other problem with the Pageable is that you let the caller's responsibility to limit the result. If you want your repository to expose only one instance (or a single value, not a Collection), the only way is the first solution or using max as shown in two other answers. – рüффп Jul 08 '22 at 19:34
  • Very good. The `Pageable` has solution for any limit and range problem. thanks – Sham Fiorin Dec 11 '22 at 15:19
25

As stated in the comments, JPQL does not support the LIMIT keyword.

You can achieve that using the setMaxResults but if what you want is just a single item, then use the getSingleResult - it throws an exception if no item is found.

So, your query would be something like:

TypedQuery<Student> query = entityManager.createQuery("SELECT s FROM Students s ORDER BY s.id DESC", Student.class);    
query.setMaxResults(1);

If you want to set a specific start offset, use query.setFirstResult(initPosition); too

dazito
  • 7,740
  • 15
  • 75
  • 117
  • 3
    Hi, I used this which also worked for me `@Query(value = "SELECT * FROM students ORDER BY id DESC LIMIT 1", nativeQuery = true) Object getLastStudentDetails();` – Madhu Jun 15 '17 at 11:44
  • 2
    ^^ There's no point using Hibernate if you are to use native query. – Siddharth Sachdeva Sep 23 '18 at 03:57
  • @SiddharthSachdeva yea, true. – Madhu Oct 15 '18 at 04:48
  • 7
    Not true. If your application has dozens of entities and dozens of queries done with either Hibernate or JPA and during maintenance you find that you need to escape once to native SQL, why would you not do it? – idarwin Jan 25 '19 at 02:29
  • @SiddharthSachdeva you should always choose the technology or approach that best suits your needs - if the "hibernate way" is not the best choice in a particular scenario we shouldn't stick with it just because it is cool. – João Matos Sep 18 '20 at 18:56
  • @JoãoMatos I would rather make a choice thinking about security implications, performance issues, code maintenance. If that takes me to learn the technology, I don't mind that. – Siddharth Sachdeva Oct 06 '20 at 00:10
16

Hello for fetching single row and using LIMIT in jpql we can tell the jpql if it's a native query.

( using - nativeQuery=true )

Below is the use

@Query("SELECT s FROM Students s ORDER BY s.id DESC LIMIT 1", nativeQuery=true)
Students getLastStudentDetails();
Manish Joshi
  • 183
  • 1
  • 3
8

You can not use Limit in HQL because Limit is database vendor dependent so Hibernate doesn't allow it through HQL query.

A way you can implement is using a subquery:

@Query("FROM Students st WHERE st.id = (SELECT max(s.id) FROM Students s)")
Students getLastStudentDetails();
David Jesus
  • 1,981
  • 2
  • 29
  • 34
7

The correct way is to write your JPA interface method like this

public interface MyRepository extends PagingAndSortingRepository<EntityClass, KeyClass> {

List<EntityClass> findTop100ByOrderByLastModifiedDesc();
}

In the method name, "100" denotes how many rows you want which you would have otherwise put in the limit clause. also "LastModified" is the column which you want to sort by.

PagingAndSortingRepository or CrudRepository, both will work for this.

For the sake of completeness, OP's interface method would be

List<Students> findTop1ByIdDesc();
NRJ
  • 1,064
  • 3
  • 15
  • 32
4

JPQL does not allow to add the limit keyword to the query generated by the HQL. You would get the following exception.

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

But don't worry there is an alternative to use the limit keyword in the query generated by the HQL by using the following steps.

Sort.by(sortBy).descending() // fetch the records in descending order

pageSize = 1 // fetch the first record from the descending order result set.

Refer the following service class

Service:

@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();
}

Your repository interface should implement the PagingAndSortingRepository

Repository:

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

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

This will add the limit keyword to you query which you can see in the console. Hope this helps.

greenhorn
  • 594
  • 6
  • 19
3

Hardcode the pagination(new PageRequest(0, 1)) to achieve fetch only one record.

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

you have to pass new PageRequest(0, 1)to fetch records and from the list fetch the first record.

Julien Kronegg
  • 4,968
  • 1
  • 47
  • 60
tk_
  • 16,415
  • 8
  • 80
  • 90
2

Here a Top Ten Service (it's a useful example)

REPOSITORY
(In the Query, I parse the score entity to ScoreTo ( DTO class) by a constructor)

@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

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

You can use something like this:

 @Repository
 public interface ICustomerMasterRepository extends CrudRepository<CustomerMaster, String> 
 {
    @Query(value = "SELECT max(c.customer_id) FROM CustomerMaster c ")
    public String getMaxId();
 }
-1

As your query is simple, you can use the solution of the accepted answer, naming your query findFirstByOrderById();

But if your query is more complicated, I also found this way without need to use a native query:

@Query("SELECT MAX(s) FROM Students s ORDER BY s.id DESC")
Students getLastStudentDetails();

Here a practical example where the named query method cannot be used.

рüффп
  • 5,172
  • 34
  • 67
  • 113
  • Fails for me because can't use `Max` without groupby ``` ERROR: column "loadtracki0_.load_tracking_status_id" must appear in the GROUP BY clause or be used in an aggregate function``` – Adam Hughes Aug 10 '22 at 15:03
  • I completely agree. – Brian Lee Jun 06 '23 at 19:00
  • If I fully understand, you get an error when using my solution? Are you sure you do not use the nativequery? my solution worked in my project with the latest release of Spring-Data JPA, the only difference is that I query two tables like in the other linked answer. – рüффп Jun 13 '23 at 17:48