JPA Projections query with GROUP BY not working with null fields - spring

I would like to replicate a query that runs happily in both MySQL and H2:
SELECT
pedido_linha.produto_id AS material,
COALESCE(pedido_linha.unidade_medida_id, "UN") AS uom,
pedido_linha.data_pedido AS referenceDate,
SUM(COALESCE(pedido_linha.quantidade, 0)) AS totalQuantity,
SUM(COALESCE(pedido_linha.valor_total, 0)) AS totalGross,
SUM(COALESCE(pedido_linha.valor_total, 0) - COALESCE(pedido_linha.valor_descontos, 0) -
COALESCE(pedido_linha.valor_impostos, 0)) AS totalNet,
SUM(COALESCE(pedido_linha.valor_custo, 0)) AS totalCogs
FROM pedido_linha
GROUP BY pedido_linha.produto_id, pedido_linha.data_pedido, COALESCE(pedido_linha.unidade_medida_id, "UN");
As an example the query runs fine in MySQL, even though some Group By fields are empty:
The following attempt has been made extract the grouped results in a JPA closed projection:
public interface AggregatedByMaterialUOMDate {
Produto getMaterial();
UnidadeMedida getUom();
LocalDate getReferenceDate();
Float getTotalQuantity();
Float getTotalGross();
Float getTotalNet();
Float getTotalCogs();
}
Along with the repository query:
#Repository
public interface PedidoLinhaRepository extends JpaRepository<PedidoLinha, PedidoLinha.PedidoLinhaCompositeKey> {
#Query("SELECT pl.produto AS material, "
+ "COALESCE(pl.unidadeMedida, :unidadeMedidaPadrao) AS uom, "
+ "pl.dataPedido AS referenceDate, "
+ "SUM(COALESCE(pl.quantidade, 0)) AS totalQuantity, "
+ "SUM(COALESCE(pl.valorTotal, 0)) AS totalGross, "
+ "SUM(COALESCE(pl.valorTotal, 0) - COALESCE(pl.valorDescontos, 0) - COALESCE(pl.valorImpostos, 0)) AS totalNet, "
+ "SUM(COALESCE(pl.valorCusto, 0)) AS totalCogs "
+ "FROM PedidoLinha pl "
+ "GROUP BY pl.produto, pl.dataPedido, COALESCE(pl.unidadeMedida, :unidadeMedidaPadrao)")
List<AggregatedByMaterialUOMDate> consolidatedSelloutByMaterialUOMDayAtLocation(#Param("unidadeMedidaPadrao") UnidadeMedida unidadeMedidaPadrao);
}
The JPQL query above returns a list with 0 elements, thus inconsistent with the native query previously shown.
We have attempted to change COALESCE(pl.unidadeMedida, :unidadeMedidaPadrao) to simply pl.unidadeMedida (both in the SELECT and GROUP BY clauses), with no success.
The query successfully returned all values only when all references to pl.unidadeMedida were completely removed from both clauses.
Below is a sample of the PedidoLinha (referenced by pl in the query) class showing the reference to UnidadeMedida:
#Getter
#Setter
#NoArgsConstructor
#RequiredArgsConstructor
#EqualsAndHashCode(of = "pedidoLinhaCompositeKey")
#Entity
public class PedidoLinha {
#EmbeddedId
#NonNull // torna campo obrigatório e parâmetro do construtor gerado pelo #Data (lombok)
private PedidoLinhaCompositeKey pedidoLinhaCompositeKey;
#Data // lombok: #ToString, #EqualsAndHashCode, #Getter on all fields #Setter on all non-final fields, and #RequiredArgsConstructor
#NoArgsConstructor
#RequiredArgsConstructor
#Embeddable
#EqualsAndHashCode
public static class PedidoLinhaCompositeKey implements Serializable {
#NonNull // torna campo obrigatório e parâmetro do construtor gerado pelo #Data (lombok)
private String id;
#ManyToOne(optional = false, fetch = FetchType.LAZY)
#NonNull // torna campo obrigatório e parâmetro do construtor gerado pelo #Data (lombok)
private Pedido pedido;
}
#ManyToOne(optional = false)
private Produto produto;
#ManyToOne
private UnidadeMedida unidadeMedida;
public UnidadeMedida getUnidadeMedida() {
return (unidadeMedida == null) ? new UnidadeMedida("UN") : unidadeMedida;
}
// rest of the entity code
}
UnidadeMedida class
#Getter
#Setter
#EqualsAndHashCode(of = "id")
#NoArgsConstructor
#Entity
public class UnidadeMedida {
#Id
private String id;
private String descricao;
public UnidadeMedida(String id) {
this.id = id;
}
}
What could be happening? I believe this could be a Hibernate bug but could not find any reference to this issue.

Turns out the key reason the JPQL query was not turning out any records was the way the implicit joins are made in the database call.
The following code represents the actual SQL code executed in the database (How to show the last queries executed on MySQL?) after running the JPQL query above:
SELECT
pedidolinh0_.produto_id as col_0_0_,
coalesce(pedidolinh0_.unidade_medida_id, 'UN') as col_1_0_,
pedidolinh0_.data_pedido as col_2_0_,
sum(coalesce(pedidolinh0_.quantidade, 0)) as col_3_0_,
sum(coalesce(pedidolinh0_.valor_total, 0)) as col_4_0_,
sum(coalesce(pedidolinh0_.valor_total, 0)-coalesce(pedidolinh0_.valor_descontos, 0)-coalesce(pedidolinh0_.valor_impostos, 0)) as col_5_0_,
sum(coalesce(pedidolinh0_.valor_custo, 0)) as col_6_0_,
produto1_.id as id1_88_, produto1_.ativo as ativo2_88_,
produto1_.data_descontinuacao as data_des3_88_,
produto1_.data_introducao as data_int4_88_,
produto1_.descricao as descrica5_88_,
produto1_.ean as ean6_88_,
produto1_.lote_minimo_requisicoes as lote_min7_88_,
produto1_.multiplo_requisicoes as multiplo8_88_,
produto1_.ncm as ncm9_88_,
produto1_.unidade_medida_padrao_id as unidade10_88_
from pedido_linha pedidolinh0_
inner join produto produto1_ on pedidolinh0_.produto_id=produto1_.id
cross join unidade_medida unidademed2_
cross join pedido pedido3_
where pedidolinh0_.unidade_medida_id=unidademed2_.id
group by
pedidolinh0_.produto_id ,
pedidolinh0_.data_pedido ,
coalesce(pedidolinh0_.unidade_medida_id, 'UN')
The issue is that the translation to SQL automatically includes a a cross join + WHERE clause pedidolinh0_.unidade_medida_id=unidademed2_.id in the query, effectively ignoring every instance where this field is null.
Even though coalescing is applied in the select statement, these records were already ignored by the where clause and thus it has no effect.
The solution was to force a left join in the JPQL query, as shown below:
SELECT
pl.produto AS material,
COALESCE(um, :unidadeMedidaPadrao) AS uom,
pl.dataPedido AS referenceDate,
SUM(COALESCE(pl.quantidade, 0)) AS totalQuantity,
SUM(COALESCE(pl.valorTotal, 0)) AS totalGross,
SUM(COALESCE(pl.valorTotal, 0) - COALESCE(pl.valorDescontos, 0) - COALESCE(pl.valorImpostos, 0)) AS totalNet,
SUM(COALESCE(pl.valorCusto, 0)) AS totalCogs
FROM PedidoLinha pl
LEFT JOIN pl.unidadeMedida um
GROUP BY pl.produto, pl.dataPedido, pl.unidadeMedida
As a result, the cross join statement is replaced by a left outer join in the final SQL executed in the database and is not included in the where clause.
Any insights as to why this design decision (use of cross join + where statement instead of left joins) was made in Hibernate would be appreciated.

Related

Order by #oneToMany field using JPA Specification

Consider the entities below -
#Entity
public class Employee {
#Id
#GeneratedValue
private long id;
private String name;
#OneToMany(mappedBy = "employee", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private List<Phone> phones; //contains both "active" & "inactive" phones
}
#Entity
public class Phone {
#Id
#GeneratedValue
private long id;
private boolean active;
private String number;
#ManyToOne(fetch = FetchType.LAZY)
private Employee employee;
}
I need to pull all the employees and sort them depending on the count of "active" phones they have.
Please note that the employee can have active as well as inactive phones. So the query I am trying to achieve is
ORDER BY (SELECT
COUNT(phone4_.employee_id)
FROM
phone phone4_
WHERE
employee4_.id = phone4_.employee_id
AND phone4_.active = true
) DESC
I am stuck with specification here because of some reason and below is the code I have used -
List<Order> orders = new ArrayList<>();
orders.add(cb.desc(cb.size(employee.get("phones"))));
cq.orderBy(orders);
When I run the code the query that's get generated is
ORDER BY (SELECT
COUNT(phone4_.employee_id)
FROM
phone phone4_
WHERE
employee4_.id = phone4_.employee_id) DESC
I am unable to add an extra AND condition to the logic. Please suggest
As specified in the Persistence API specification:
4.6.16 Subqueries
Subqueries may be used in the WHERE or HAVING clause.
JPA doesn't support subqueries in the order by clause, nor in the select clause.
Hibernate ORM, though, supports them in the SELECT and WHERE clauses.
So you cannot write that query and being JPA compliant.
This HQL should work though and it's covered by Hibernate ORM:
SELECT e1, (SELECT count(p)
FROM Phone p
WHERE p.active = true AND p.employee = e1) as activeCount
FROM Employee e1
ORDER BY activeCount DESC
Surprisingly, writing this query with criteria doesn't work:
CriteriaBuilder builder = ormSession.getCriteriaBuilder();
CriteriaQuery<Object> criteria = builder.createQuery();
Root<Employee> root = criteria.from( Employee.class );
Subquery<Long> activePhonesQuery = criteria.subquery( Long.class );
Root<Phone> phoneRoot = activePhonesQuery.from( Phone.class );
Subquery<Long> phonesCount = activePhonesQuery
.select( builder.count( phoneRoot ) )
.where( builder.and( builder.isTrue( phoneRoot.get( "active" ) ), builder.equal( phoneRoot.get( "employee" ), root ) ) );
criteria.multiselect( root, phonesCount )
.orderBy( builder.desc( phonesCount ) );
The reason is that, Hibernate ORM tries to expand the subquery in the order by clause instead to refer to an alias. And as I mentioned before, this is not supported.
I think the HQL is the easiest option if you don't want to use native queries.

How to handle a Hibernate multi-field search query on nullable child entities

Using Spring Boot Web & Data JPA (2.3.1) with QueryDSL with PostgreSQL 11, we are trying to implement a custom search for a UI table on an entity with two #ManyToOne child entities. The idea is to be able to provide a single search input field and search for that string (like or contains ignore case) across multiple String fields across the entities' fields and also provide paging. During UI POCs, we were originally pulling the entire list and having the web UI provide this exact search functionality but that will not be sustainable in the future.
My original thought was something to this effect:
/devices?externalId={{val}}&site.externalId={{val}}&organization.name={{val}}&size=10
but the more human readable intent was:
externalId={{val}} OR site.externalId={{val}} OR organization.name={{val}} WITH size=10
I tried implementing it with QueryDSL's Web Bindings (hence the above example) with a QuerydslBinderCustomizer but it didn't work. Then I realized that it doesn't provide much for this particular situation so I moved to a JpaRepository with an #Query and shortened the URL to.
/devices?search={{val}}&size=10
Either way, what seems to be happening is that if, for example, device.site is null, the entire result is always zero. Even if device.organization or device.site is null, I would expect results where the device.externalId matches the search value criteria. If I remove support for site.externalId, then it works; I get results matching the device.externalId. I also tried this on a database with devices with non-null site references and that also worked. So the issue seems to be centered around null child entities.
Quick Scenario:
Note: Device in JSON format and id's in non-UUID format for brevity.
{
"id" : "a",
"externalId" : "VTD1002",
"site" : null
},
{
"id" : "b",
"externalId" : "VTD_1000",
"site" : { "externalId" : "VTS_1000" }
},
{
"id" : "c",
"externalId" : "VFD_1000"
"site" : { "externalId" : "VFS_1000" }
}
Pseudo Tests:
search = "t" -> resulting IDs = a, b
Only a and b have a T
search = "Z" -> resulting IDs =
None have Z
search = "1" -> resulting IDs = a, b, c
All have a 1
search = "2" -> resulting IDs = a
Only a has a 2
Entities
Note: redacted from original for brevity
#Entity
#Data #Builder
#Table(name = "devices")
#EqualsAndHashCode(of = "id")
#NoArgsConstructor #AllArgsConstructor
public class Device {
#Id
#GeneratedValue
private UUID id;
private String externalId;
#ManyToOne
private Product product;
#ManyToOne
private Site site;
}
#Entity
#Data #Builder
#Table(name = "organizations")
#EqualsAndHashCode(of = "id")
#NoArgsConstructor #AllArgsConstructor
public class Organization {
#Id
#GeneratedValue
private UUID id;
private String name;
#ToString.Exclude
#OneToMany(mappedBy = "organization")
private Set<Device> devices;
}
#Entity
#Data #Builder
#NoArgsConstructor
#AllArgsConstructor
#EqualsAndHashCode(of = "id")
#Table(name = "sites")
public class Site {
#Id
#GeneratedValue
private UUID id;
private String externalId;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "site")
private Set<Device> devices;
}
Here's the latest I've tried without luck:
#Query("from Device d where (d.externalId like lower(:search)) or (d.site.id is not null and d.site.externalId like lower(:search)) or (d.organization.id is not null and d.organization.externalId like lower(:search))")
Page<Device> search(String search, Pageable pageable);
The idea was to check for the device's site_id reference on the table before even trying to evaluate the Site's externalId. Removing the is not null didn't make a difference and going d.site is not null also didn't work.
I think what's happening is that the native SQL that is generated is causing things to go awry. I suspect the issue is in the cross joins but after a few days of searching, any clues or insights would be appreciated.
select
device0_.id as id1_3_,
device0_.external_id as external3_3_,
device0_.organization_id as organiz13_3_,
device0_.site_id as site_id15_3_,
from
devices device0_ cross
join
sites site1_ cross
join
organizations organizati2_
where
device0_.site_id=site1_.id
and device0_.organization_id=organizati2_.id
and (
device0_.external_id like lower(?)
or (
device0_.site_id is not null
)
and (
site1_.external_id like lower(?)
)
or (
device0_.organization_id is not null
)
and (
organizati2_.external_id like lower(?)
)
) limit ?
The problem as you can see is that Hibernate uses inner joins for your implicit joins, which is forced onto it by JPA. Having said that, you will have to use left joins like this to make this null-aware stuff work
#Query("from Device d left join d.site s left join d.organization o where (d.externalId like lower(:search)) or (s.id is not null and s.externalId like lower(:search)) or (o.id is not null and o.externalId like lower(:search))")
Page<Device> search(String search, Pageable pageable);
With #christian-beikov help, I was able to get pass the nullability piece. However, I had to modify the query to get it working all the way through to the web controller.
#Query("select d from Device d left join d.site s left join d.organization o "
+ "where lower(d.externalId) like concat('%', lower(:search),'%')"
+ "or lower(s.externalId) like concat('%', lower(:search),'%')"
+ "or lower(o.externalId) like concat('%', lower(:search),'%')")
Page<Device> focusedSearchByValue(String search, Pageable pageable);
I had to add select d so that I would get back a list of Devices and not of Object.
I then had to make sure both the search term and the target column value of were in lower-case so that the comparison is "apples to apples".
Finally, I added the equivalent of ilike %{search}% because apparently there is no HQL support for ilike.
As a side note, for experimentation purposes, I also looked at what this equivalent approach generated to further explore the problem.
Page<Device> findAllByExternalIdContainsOrSiteExternalIdContainsOrOrganizationNameContains(String v1, String v2, String v3, Pageable pageable);

Select latest record for each id spring jpa

I have an entity like this:
#Entity
class Point{
#EmbeddedId
private PointIdentity pointIdentity;
private float latitude;
private float longitude;
#Embeddable
public static class PointIdentity implements Serializable {
private Long id;
private ZonedDateTime timestamp;
}
}
There is EmbeddedId, so in "id" column can be multiple records with the same ids.
And I need to get latest record for each id, using CriteriaQuery and JPA specifications I think, but don't know how.
In SQL, this would be something like this:
SELECT id, MAX(timestamp)
FROM geodata
GROUP BY id
Is there any way to do it?
Any help, thanks.
You can easily write a JPQL query:
TypedQuery<Object[]> query = entityManager.createQuery(
"select p.pointIdentity.id, max(p.pointIdentity.timestamp) from Point p group by p.pointIdentity.id",
Object[].class);
List<Object[]> results = query.getResultList();
which translates to:
select
point0_.id as col_0_0_,
max(point0_.timestamp) as col_1_0_
from
point point0_
group by
point0_.id
Alternatively, you can use criteria query:
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<Object[]> query = criteriaBuilder.createQuery(Object[].class);
Root<Point> point = query.from(Point.class);
query.groupBy(point.get("pointIdentity").get("id"));
query.multiselect(
point.get("pointIdentity").get("id"),
criteriaBuilder.max(point.get("pointIdentity").get("timestamp"))
);
TypedQuery<Object[]> typedQuery = entityManager.createQuery(query);
List<Object[]> results = typedQuery.getResultList();
which produces identical SQL.

Spring Data JPA query to select all value from one join table

I have problem to select all value from one table and few other columns using Spring Data JPA. I am using PostgreSql database and when I send query through PgAdmin I get values I want, but if I use it in Spring Boot Rest returns only one table values (subquery not working). What I am doing wrong?
#Query(value = "SELECT item.*, MIN(myBid.bid) AS myBid, (SELECT MIN(lowestBid.bid) AS lowestbid FROM bids lowestBid WHERE lowestBid.item_id = item.item_id GROUP BY lowestBid.item_id) FROM item JOIN bids myBid ON item.item_id = myBid.item_id WHERE myBid.user_id = :user_id GROUP BY item.item_id", nativeQuery = true)
public List<Item> findAllWithDescriptionQuery(#Param("user_id") UUID userId);
Added Item class
#Data
#Entity(name = "item")
public class Item {
#Id
#GeneratedValue
private UUID itemId;
#NotNull
#Column(name = "title")
#Size(max = 255)
private String title;
#NotNull
#Column(name = "description")
private String description;
#NotNull
#Column(name = "created_user_id")
private UUID createdUserId;
}
The result from your native query cannot simply be mapped to entities due to the in-database aggregation performed to calculate the MIN of own bids, and the MIN of other bids. In particular, your Item entity doesn't carry any attributes to hold myBid or lowestbid.
What you want to return from the query method is therefore a Projection. A projection is a mere interface with getter methods matching exactly the fields returned by your query:
public interface BidSummary {
UUID getItem_id();
String getTitle();
String getDescription();
double getMyBid();
double getLowestbid();
}
Notice how the query method returns the BidSummary projection:
#Query(value = "SELECT item.*, MIN(myBid.bid) AS myBid, (SELECT MIN(lowestBid.bid) AS lowestbid FROM bids lowestBid WHERE lowestBid.item_id = item.item_id GROUP BY lowestBid.item_id) FROM item JOIN bids myBid ON item.item_id = myBid.item_id WHERE myBid.user_id = :user_id GROUP BY item.item_id", nativeQuery = true)
public List<BidSummary> findOwnBids(#Param("user_id") UUID userId);
Return type is List of Item objects and the query specified is having columns which are not part of return object. I recommend using appropriate Entity which full-fills your response type.

Pagination error in SpringBoot "Incorrect syntax near '#P0'" [duplicate]

I'm using Spring Data JPA, and when I use #Query to to define a query WITHOUT Pageable, it works:
public interface UrnMappingRepository extends JpaRepository<UrnMapping, Long> {
#Query(value = "select * from internal_uddi where urn like %?1% or contact like %?1%",
nativeQuery = true)
List<UrnMapping> fullTextSearch(String text);
}
But if I add the second param Pageable, the #Query will NOT work, and Spring will parse the method's name, then throw the exception No property full found. Is this a bug?
public interface UrnMappingRepository extends JpaRepository<UrnMapping, Long> {
#Query(value = "select * from internal_uddi where urn like %?1% or contact like %?1%",
nativeQuery = true)
Page<UrnMapping> fullTextSearch(String text, Pageable pageable);
}
You can use pagination with a native query. It is documented here: Spring Data JPA - Reference Documentation
"You can however use native queries for pagination by specifying the count query yourself:
Example 59. Declare native count queries for pagination at the query method using #Query"
public interface UserRepository extends JpaRepository<User, Long> {
#Query(value = "SELECT * FROM USERS WHERE LASTNAME = ?1",
countQuery = "SELECT count(*) FROM USERS WHERE LASTNAME = ?1",
nativeQuery = true)
Page<User> findByLastname(String lastname, Pageable pageable);
}
A similar question was asked on the Spring forums, where it was pointed out that to apply pagination, a second subquery must be derived. Because the subquery is referring to the same fields, you need to ensure that your query uses aliases for the entities/tables it refers to. This means that where you wrote:
select * from internal_uddi where urn like
You should instead have:
select * from internal_uddi iu where iu.urn like ...
Considering that the UrnMapping class is mapped to the internal_uddi table, I would suggest this:
#Repository
public interface UrnMappingRepository extends JpaRepository<UrnMapping, Long> {
#Query(value = "select iu from UrnMapping iu where iu.urn like %:text% or iu.contact like %:text%")
Page<UrnMapping> fullTextSearch(#Param("text") String text, Pageable pageable);
}
Please note that you might have to turn off native queries with dynamic requests.
With #Query , we can use pagination as well where you need to pass object of Pageable class at end of JPA method
For example:
Pageable pageableRequest = new PageRequest(page, size, Sort.Direction.DESC, rollNo);
Where,
page = index of page (index start from zero)
size = No. of records
Sort.Direction = Sorting as per rollNo
rollNo = Field in User class
UserRepository repo
repo.findByFirstname("John", pageableRequest);
public interface UserRepository extends JpaRepository<User, Long> {
#Query(value = "SELECT * FROM USER WHERE FIRSTNAME = :firstname)
Page<User> findByLastname(#Param("firstname") String firstname, Pageable pageable);
}
Please reference :Spring Data JPA #Query, if you are using Spring Data JPA version 2.0.4 and later. Sample like below:
#Query(value = "SELECT u FROM User u ORDER BY id")
Page<User> findAllUsersWithPagination(Pageable pageable);
Declare native count queries for pagination at the query method by using #Query
public interface UserRepository extends JpaRepository<User, Long> {
#Query(value = "SELECT * FROM USERS WHERE LASTNAME = ?1",
countQuery = "SELECT count(*) FROM USERS WHERE LASTNAME = ?1",
nativeQuery = true)
Page<User> findByLastname(String lastname, Pageable pageable);
}
Hope this helps
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods
Rewrite your query to:
select iu from internal_uddi iu where iu.urn....
description: http://forum.spring.io/forum/spring-projects/data/126415-is-it-possible-to-use-query-and-pageable?p=611398#post611398
I found it works different among different jpa versions, for debug, you'd better add this configurations to show generated sql, it will save your time a lot !
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
for spring boot 2.1.6.RELEASE, it works good!
Sort sort = new Sort(Sort.Direction.DESC, "column_name");
int pageNumber = 3, pageSize = 5;
Pageable pageable = PageRequest.of(pageNumber - 1, pageSize, sort);
#Query(value = "select * from integrity_score_view " +
"where (?1 is null or data_hour >= ?1 ) " +
"and (?2 is null or data_hour <= ?2 ) " +
"and (?3 is null or ?3 = '' or park_no = ?3 ) " +
"group by park_name, data_hour ",
countQuery = "select count(*) from integrity_score_view " +
"where (?1 is null or data_hour >= ?1 ) " +
"and (?2 is null or data_hour <= ?2 ) " +
"and (?3 is null or ?3 = '' or park_no = ?3 ) " +
"group by park_name, data_hour",
nativeQuery = true
)
Page<IntegrityScoreView> queryParkView(Date from, Date to, String parkNo, Pageable pageable);
you DO NOT write order by and limit, it generates the right sql
I had the same issue - without Pageable method works fine.
When added as method parameter - doesn't work.
After playing with DB console and native query support came up to decision that method works like it should. However, only for upper case letters.
Logic of my application was that all names of entity starts from upper case letters.
Playing a little bit with it. And discover that IgnoreCase at method name do the "magic" and here is working solution:
public interface EmployeeRepository
extends PagingAndSortingRepository<Employee, Integer> {
Page<Employee> findAllByNameIgnoreCaseStartsWith(String name, Pageable pageable);
}
Where entity looks like:
#Data
#Entity
#Table(name = "tblEmployees")
public class Employee {
#Id
#Column(name = "empID")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#NotEmpty
#Size(min = 2, max = 20)
#Column(name = "empName", length = 25)
private String name;
#Column(name = "empActive")
private Boolean active;
#ManyToOne
#JoinColumn(name = "emp_dpID")
private Department department;
}
When using nativeQuery that is having (nativeQuery = true), you may do the pagination yourself in the query by adding (LIMIT :sizeValue OFFSET :page)
Note:
Your page value passed to this method should be offset * size
Example
#Query(value = "SELECT * FROM person " +
"LIMIT ?1 OFFSET ?2", nativeQuery = true)
Optional<List<TDriverJob>> findPersons(int size, int page);
I tried all above solution and non worked , finally I removed the Sorting from Pagination and it worked
the following tutorial helped me
-> https://www.baeldung.com/spring-data-jpa-query
At this point 4.3. Spring Data JPA Versions Prior to 2.0.4
VERY IMPORTANT to add \ n-- #pageable \ n
Without this I was wrong
Also the pagination setting must be without ordering
PageRequest paginaConf = new PageRequest ((param1 - 1)
, param2);
Finally to convert the Page <Object []>
Page <Object []> list = myQueryofRepo ();
List <XXXModel> lstReturn = myConversor (list.getContent ());
Page <XXXModel> ret = new PageImpl <XXXModel> (lstReturn, pageConf, param2);
This bugged me for a while but I managed with a very smooth solution.
The challenge is JPA did not automatically detect the count query so I resolved to use the countName which according JPA docs Returns the name of the javax.persistence.NamedQuery to be used to execute count queries when pagination is used. Will default to the named query name configured suffixed by .count.
So I created a named query
#NamedNativeQuery(
name = "[queryName].count",
query = [your count query],
resultSetMapping = "[query resultSetMapping name]"
)
}
As indicated, the count query should be suffixed with .count
Count query returns Long so add the resultSetMapping as
#SqlResultSetMapping(
name="[query resultSetMapping name]",
columns={#ColumnResult(name="count", type = Long.class)})
Then in your repository, use the count query as indicated below
#Query(countName ="[queryName].count" , nativeQuery = true)
Page<Object> [mainQuery](...params)
Hope this helps!

Resources