Limiting resultset using #Query anotations - jpa

I have a query where I want to limit the size of the resultset. As JPA does not support 'LIMIT', I have been trying to work out the correct approach. I have tried:
#Query("SELECT w FROM WardTransaction w WHERE w.visit = :visit ORDER BY w.admissionDateTime DESC").setMaxResults(1)
public WardTransaction findCurrent(#Param("visit") Visit visit);
Which is not correct. I am just looking for some guidance as to the correct syntax.
My repository code is:

#Query("SELECT w FROM WardTransaction w WHERE w.visit = :visit ORDER BY w.admissionDateTime DESC")
The above is the query method definition, to setMaxResult in your query you need to use Pageable object as it follows.
#Query("SELECT w FROM WardTransaction w WHERE w.visit = :visit ORDER BY w.admissionDateTime DESC")
public void List<Entities> findAll(Pageable pageable)
JPA repository must implement SimpleJpaRepository
Set Pageable object as it follows>
Pageable pageSpecification = PageRequest(int page, int size)
Combination for Pageable and SimpleJpaRepository is the solution.
Take a look here
If you are using EntityManager and NamedQueries, there is a method setMaxResult that apply to Query object, but it is a different story.

Related

How to find top N elements in Spring Data Jpa?

In Spring Data Jpa to get first 10 rows I can do this findTop10By...(). In my case the number or rows is not defined and comes as a parameter.
Is there something like findTopNBy...(int countOfRowsToGet)?
Here is another way without native query. I added Pageable as a parameter to the method in the interface.
findAllBySomeField(..., Pageable pageable)
I call it like this:
findAllBySomeField(..., PageRequest.of(0, limit)) // get first N rows
findAllBySomeField(..., Pageable.unpaged()) // get all rows
I don't know of a way to do exactly what you want, but if you are open to using #Query in your JPA repository class, then a prepared statement is one alternative:
#Query("SELECT * FROM Entity e ORDER BY e.id LIMIT :limit", nativeQuery=true)
Entity getEntitiesByLimit(#Param("limit") int limit);
Did it by using pagination, as described in the first answer. Just adding a more explicit example.
This example will give you the first 50 records ordered by id.
Repository:
#Repository
public interface MyRepository extends JpaRepository<MyEntity, String> {
Page<MyEntity> findAll(Pageable pageable);
}
Service:
#Service
public class MyDataService {
#Autowired
MyRepository myRepository;
private static final int LIMIT = 50;
public Optional<List<MyEntity>> getAllLimited() {
Page<MyEntity> page = myRepository.findAll(PageRequest.of(0, LIMIT, Sort.by(Sort.Order.asc("id"))));
return Optional.of(page.getContent());
}
}
Found the original idea here:
https://itqna.net/questions/16074/spring-data-jpa-does-not-recognize-sql-limit-command
(which will also link to another SO question btw)

Spring JPA repository casting error when using JPQL

I have a PagingAndSorting JPA repository declared. I am using the #Query annotation.
I am getting an exception when I call the get() method on an Optional object from the findById(id) method of the repository.
The weird thing is it only happens when I use JPQL.
The code works if my query is native:
#Override
public BatchDto findById(String id) {
Optional<Batch> findResult = this.batchRepository.findById(id);
if (!findResult.isPresent()) return null;
Batch entity = findResult.get(); **<-------- Cast Exception Here**
BatchDto dto = this.mapper.toDto(entity, BatchDto.class);
List<BatchTransaction> transactions = entity.getTransactions();
dto.setTransactionDtos(mapper.toListDto(transactions, TransactionDto.class));
return dto;
}
Inspecting the findResult object with a breakpoint - I can see:
Optional[net.domain.data.batch#4b8bb6f]
when I have nativeQuery = true in the #Query annotation.
#Query(value = Sql.FindBatchById, nativeQuery = true)
Here is the query being used:
SELECT DISTINCT(B.batchNumber), COUNT(B.batchNumber) as TransactionCount FROM BATCH B WHERE B.batchReferenceNumber = :id GROUP BY B.batchNumber
However if I change it to JPQL and remove the nativeQuery=true attribute - the findResult is
Optional[[Ljava.lang.Object;#76e04327].
and I get a ClassCastException:
java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to net.domain.data.batch
So bottom line - this works when specify nativeQuery=true and fails when I try to use JPQL.
I would prefer not to specify nativeQuery as we will eventually port this db to Oracle.
First of all the query shown below doesn't return a single Batch instance. Since there are distinct and count aggregate functions, the query will return a List of aggregates.
To be able to read that statistics you can add appropriate method into the batchRepository. Something like this:
#Query("SELECT DISTINCT(B.batchNumber) as dist, COUNT(B.batchNumber) as cnt FROM BATCH B GROUP BY B.batchNumber")
List<Map<Long, Long>> findStatistics();
and then iterate through the list.
UPD
If the id parameter exactly guarantee that will return a single record, you can change a return type to a Map
#Query("SELECT DISTINCT(B.batchNumber) as dist, COUNT(B.batchNumber) as cnt FROM BATCH B WHERE B.batchReferenceNumber = :id GROUP BY B.batchNumber")
Map<Long, Long> findStatisticsById(#Param("id") Long id);

JPA eclipselink global batch fetch?

when joining I get one select per row. Solution is batch fetch but I dont want that annotation everywhere...
http://eclipse.org/eclipselink/documentation/2.4/jpa/extensions/a_batchfetch.htm
Why do I even need this? One select per row is awful... How can I set this globally? Cheers
Maybe not the ideal solution, but you may try to use JPA hints along with Java generics:
public <T> TypedQuery<T>
createBatchQuery(String ql, Class<T> clazz, String type, String size, String relation) {
return em.createQuery(jpql, clazz)
.setHint(QueryHints.BATCH_TYPE, type)
.setHint(QueryHints.BATCH_SIZE, size)
.setHint(QueryHints.BATCH, relation);
}
The above query may then be used globally and extended with concrete query implementations according to you needs, i.e.
String jpql = "SELECT c FROM Country c WHERE c.name = :name"; // or #NamedQuery
TypedQuery<Country> q = createBatchQuery(jpql, Country.class, "JOIN", "64", "c.cities");
q.setParameter("name", "Australia");
Country c = q.getSingleResult();
Articles on this topic:
Batch fetching - optimizing object graph loading
EclipseLink/Examples/JPA/QueryOptimization

Finding Maximum query result using spring-data-jpa #Query annotation

Is is possible to limit query results using .setMaxResults(1).getResultList() and #Query - something like:
#Query("SELECT l FROM LocationLog l WHERE l.visit = :visit ORDER BY l.eventTime").setMaxResults(1).getResultList()
public LocationLog findLast(#Param("visit") Visit visit);
This code is not correct as the setMaxResults and so on is outside the #Query?l
You cannot set pagination options in #Query annotation, but it can be done in other way. You can change your method declaration to
public LocationLog find(#Param("visit") Visit visit, Pageable pageable);
Then you will be able to pass additional argument to query invocation:
Pageable firstFive = new PageRequest(0, 5);
LocationLog locLog = locationLogDao.find(visit, firstFive);
There's also option to know current context of the query results you can use Page interface changing method return type to Page<LocationLog>. You can find all necessary informations here.

How to have a #Query with a possibly null parameter

I have a #Query that gives me trouble. I'd like to get all webpages that have a given parent, even if the given parent is null.
The parent can be set but it can also not be set.
I've been trying this:
#Query("SELECT w FROM Webpage w WHERE w.parent = :parent OR (parent IS NULL AND :parent.id < '1')) ORDER BY w.listOrder")
public Page<Webpage> findByParent(#Param("parent") Webpage parent, Pageable page);
And this:
#Query("SELECT w FROM Webpage w WHERE w.parent = :parent OR (parent IS NULL AND :parent IS NULL)) ORDER BY w.listOrder")
public Page<Webpage> findByParent(#Param("parent") Webpage parent, Pageable page);
But I get the exception:
Caused by: java.lang.IllegalArgumentException: Validation failed for query for method public abstract
com.thalasoft.learnintouch.data.jpa.domain.Webpage
com.thalasoft.learnintouch.data.jpa.repository.WebpageRepository.findByParentAndNameAndNotGarbage(com.thalasoft.learnintouch.data.jpa.domain.Webpage,java.lang.String)!
I'm using the spring 3.2.9.RELEASE and spring-data-jpa 1.6.1.RELEASE version.
Any idea on how I can select on a given parent, even if the given parent is null ?
Kind Regards,
Stephane Eybert
If the validation on the provider will not let you pass nulls, you can split the query into two. You'll have to have logic in the caller to figure out which one to call.
Query1 (param is not null):
#Query("SELECT w FROM Webpage w WHERE w.parent = :parent ORDER BY w.listOrder")
public Page<Webpage> findByParent(#Param("parent") Webpage parent, Pageable page);
Query2 (param is null):
#Query("SELECT w FROM Webpage w WHERE w.parent is null ORDER BY w.listOrder")
public Page<Webpage> findByNullParent(Pageable page);
I would recommend reading some more of the docs on your provider to see how to pass nulls in.