spring data jpa Pageable Sort Error - sorting

I use spring boot (1.3.5), spring-data, spring-data-jpa, JPA(hibernate/hsqldb).
The code:
POM:
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
The code for configuration:
#Bean
public PageableHandlerMethodArgumentResolver pageableHandlerMethodArgumentResolver(
SortHandlerMethodArgumentResolver sortHandlerMethodArgumentResolver) {
PageableHandlerMethodArgumentResolver phmar = new PageableHandlerMethodArgumentResolver(
sortHandlerMethodArgumentResolver);
phmar.setOneIndexedParameters(false);
phmar.setPageParameterName("page");
phmar.setSizeParameterName("size");
phmar.setMaxPageSize(20);
return phmar;
}
#Bean
public SortHandlerMethodArgumentResolver sortHandlerMethodArgumentResolver() {
SortHandlerMethodArgumentResolver shmar = new SortHandlerMethodArgumentResolver();
shmar.setSortParameter("sort");
return shmar;
}
The controller:
#RequestMapping(value = { "/List", "" })
public String list(Model model, #RequestParam(required = false) String searchString,
#SortDefault(sort = "code", direction = Direction.ASC) #PageableDefault(page = 0, size = 20) Pageable pageable) {
I try
#PageableDefault(page = 0, size = 20, sort = "code", direction = Direction.ASC)
too, but it does not work.
Page<T> page;
if (!isEmpty(searchString))
page = service.search(searchString, pageable); // <-- ERROR
else
page = service.findAll(pageable); // <-- OK
The service (simplified):
#Override
#Transactional(readOnly = true)
public Page<T> search(String str, Pageable pageable) {
return repository.search(str, pageable);
}
The repository (simplified):
#Repository
public interface EntityRepository extends JpaRepository<T, Integer> {
(...)
#Query(value = "SELECT a FROM #{#entityName} a WHERE a.code LIKE CONCAT('%', :str, '%') OR UPPER(a.name) LIKE UPPER(CONCAT('%', :str, '%'))")
Page<T> search(#Param("str") String str, Pageable pageable);
I try whith the same #Query but return List<> intead Page<> without Pageable pageable parameter, and it works with this #Query
test:
#Query(value = "SELECT a FROM #{#entityName} a WHERE a.code LIKE CONCAT('%', :str, '%') OR UPPER(a.name) LIKE UPPER(CONCAT('%', :str, '%'))")
List<T> search(#Param("str") String str);
When I call findAll(pageable) it works but when I call search(str, pageable) (str="AT") it does not work.
Browser output:
Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.
Wed May 25 15:16:24 CEST 2016
There was an unexpected error (type=Internal Server Error, status=500).
org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected token: : near line 1, column 151 [SELECT a FROM prueba.entity.AccountType a WHERE a.code LIKE CONCAT('%', :str, '%') OR UPPER(a.name) LIKE UPPER(CONCAT('%', :str, '%')) order by a.code: ASC asc]; nested exception is java.lang.IllegalArgumentException: org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected token: : near line 1, column 151 [SELECT a FROM prueba.entity.AccountType a WHERE a.code LIKE CONCAT('%', :str, '%') OR UPPER(a.name) LIKE UPPER(CONCAT('%', :str, '%')) order by a.code: ASC asc]
The SQL is invalid! Extra ":" and duplicate "ASC asc".
Console output:
Hibernate:
select
count(accounttyp0_.id) as col_0_0_
from
account_type accounttyp0_
where
accounttyp0_.code like ('%'||?||'%')
or upper(accounttyp0_.name) like upper(('%'||?||'%'))
mo.h.hql.internal.ast.ErrorCounter line 1:151: unexpected token: :
mo.h.hql.internal.ast.ErrorCounter line 1:151: unexpected token: :
antlr.NoViableAltException: unexpected token: :
at org.hibernate.hql.internal.antlr.HqlBaseParser.atom(HqlBaseParser.java:3694) [hibernate-core-4.3.11.Final.jar:4.3.11.Final]
(more and more)
near line 1, column 151 [SELECT a FROM prueba.entity.AccountType a WHERE a.code LIKE CONCAT('%', :str, '%') OR UPPER(a.name) LIKE UPPER(CONCAT('%', :str, '%')) order by a.code: ASC asc]; nested exception is java.lang.IllegalArgumentException: org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected token: : near line 1, column 151 [SELECT a FROM prueba.entity.AccountType a WHERE a.code LIKE CONCAT('%', :str, '%') OR UPPER(a.name) LIKE UPPER(CONCAT('%', :str, '%')) order by a.code: ASC asc]] with root cause
org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected token: : near line 1, column 151 [SELECT a FROM prueba.entity.AccountType a WHERE a.code LIKE CONCAT('%', :str, '%') OR UPPER(a.name) LIKE UPPER(CONCAT('%', :str, '%')) order by a.code: ASC asc]
at org.hibernate.hql.internal.ast.QuerySyntaxException.convert(QuerySyntaxExcept ion.java:91) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]

It seems that during query generation u got that exception.For Pagination use spring-data-jpa you have to implement PagingAndSortingRepository Interface.
You have to write a method which takes parameters as Pageable page as and you can pass new PageRequest(0,size) as argument to Pageable and size is the no of records you want to fetch.
Don't use #Query instead you can write method which is easy and you
can use all the supported key words in spring-data-jpa.you can check
below for supported keywords.
http://docs.spring.io/spring-data/jpa/docs/1.4.3.RELEASE/reference/html/jpa.repositories.html

Related

Spring data jpa #Query - Cannot create an "AS" alias for a result map

I map the result of the following JPQL-Query directly to a SpecialCustomDto object instead of the used javax.persistency entity object MyEntity. But I do not know, how to access the COUNT(DISTINCT e.attributeB), which will be mapped to the SpecialCustomDto.
This is the query.
#Repository
public interface MyEntityRepository extends JpaRepository<MyEntity, Long> {
#Query("SELECT new com.test.SpecialCustomDto(e.attributeA, COUNT(DISTINCT e.attributeB)) as specialCustomDto "
+ "FROM MyEntity e WHERE 5 = specialCustomDto.count GROUP BY e.attributeA")
List<SpecialCustomDto> getSpecialCustomDtos();
}
As soon as I start the spring-boot application, Hibernate throws me following error:
Caused by: java.lang.IllegalArgumentException: org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected token: as near line 1, column...
I don't know how to access the aggregated COUNT(DISTINCT e.attributeB) element of the newly created SpecialCustomDto. Without the additional WHERE-clause, the mapping works as expected.
Aggregate functions can be used as condition using HAVING. The same as in native SQL.
SELECT new com.test.SpecialCustomDto(e.attributeA, COUNT(e.attributeB))
FROM MyEntity e
GROUP BY e.attributeA
HAVING COUNT(e.attributeB) = 5
Remove the alias, move the condition to a HAVING-clause since it operates on an aggregate-value and just put the count-expression in there.
public interface MyEntityRepository extends JpaRepository<MyEntity, Long> {
#Query("SELECT new com.test.SpecialCustomDto(e.attributeA, COUNT(DISTINCT e.attributeB)) "
+ "FROM MyEntity e "
+ "GROUP BY e.attributeA "
+ "HAVING COUNT(DISTINCT e.attributeB) = 5")
List<SpecialCustomDto> getSpecialCustomDtos();
}
Note: The #Repository is superfluous.

spring boot data #query to DTO

I want to assign the result of a query to a DTO object. The DTO looks like this:
#Getter
#Setter
#NoArgsConstructor
public class Metric {
private int share;
private int shareholder;
public Metric(int share, int shareholder) {
this.share = share;
this.shareholder = shareholder;
}
}
And the query looks like the following:
#RepositoryRestResource(collectionResourceRel = "shareholders", path = "shareholders")
public interface ShareholderRepository extends PagingAndSortingRepository<Shareholder, Integer> {
#Query(value = "SELECT new com.company.shareholders.sh.Metric(SUM(s.no_of_shares),COUNT(*)) FROM shareholders s WHERE s.attend=true")
Metric getMetrics();
}
However, this didn't work, as I got the following exception:
Caused by:org.hibernate.QueryException: could not resolve property: no_of_shares of:com.company.shareholders.sh.Shareholder[SELECT new com.company.shareholders.sh.Metric(SUM(s.no_of_shares),COUNT(*)) FROM com.company.shareholders.sh.Shareholder s WHERE s.attend=true]
In my project I've used projections to this like shown below:
#Repository
public interface PeopleRepository extends JpaRepository<People, Long> {
#Query(value = "SELECT p.name AS name, COUNT(dp.people_id) AS count " +
"FROM people p INNER JOIN dream_people dp " +
"ON p.id = dp.people_id " +
"WHERE p.user_id = :userId " +
"GROUP BY dp.people_id " +
"ORDER BY p.name", nativeQuery = true)
List<PeopleDTO> findByPeopleAndCountByUserId(#Param("userId") Long userId);
#Query(value = "SELECT p.name AS name, COUNT(dp.people_id) AS count " +
"FROM people p INNER JOIN dream_people dp " +
"ON p.id = dp.people_id " +
"WHERE p.user_id = :userId " +
"GROUP BY dp.people_id " +
"ORDER BY p.name", nativeQuery = true)
Page<PeopleDTO> findByPeopleAndCountByUserId(#Param("userId") Long userId, Pageable pageable);
}
The interface to which the result is projected:
public interface PeopleDTO {
String getName();
Long getCount();
}
The fields from the projected interface must match the fields in this entity. Otherwise field mapping might break.
Also if you use SELECT table.column notation always define aliases matching names from entity as shown in example.
In your case change #Query like shown below:
#Query(value = "SELECT new " +
"SUM(s.no_of_shares) AS sum,COUNT(*) AS count FROM " +
"shareholders s WHERE s.attend=true", nativeQuery = true)
MetricDTO getMetrics();
And create interface MetricDTO like shown below:
public interface MetricDTO {
Integer getSum();
Long getCount();
}
Also make sure the return type of getSum() and getCount() is correct this may vary based not database.
First, you can have a look at the Spring Data JPA documentation, you can find some help at this section : Class-based Projections (DTOs).
There is also a paragraph titled Avoid boilerplate code for projection DTOs, where they advise you to use Lombok's #Value annotation, to produce an immutable DTO. This is similar to Lombok's #Data annotation, but immutable.
If you apply it to your example, the source will look like :
#Value
public class MetricDto {
private int share;
private int shareholder;
}
Then, as your query is a NativeQuery, specifiy it in your Spring Data Repository.
You can find help in the documentation : Native Queries.
You will need something like :
#Query(value = "SELECT new
com.company.shareholders.sh.MetricDto(SUM(s.no_of_shares),COUNT(*)) FROM
shareholders s WHERE s.attend=true", nativeQuery = true)
MetricDto getMetrics();
Query query = sessionFactory.getCurrentSession()
.createNativeQuery(stringQuery).unwrap(org.hibernate.query.Query.class);
((NativeQueryImpl) query).setResultTransformer(new AliasToBeanResultTransformer(DtoClass.class));
You are writing a mixed query of native and jpql; no_of_shares is your column name in the database, but jpa is expecting you to provide not native syntax so try to replace no_of_shares with the corresponding field in your entity class. Or just add nativeQuery = true to make jpa understand it's a native query.

Native Query Like Format Issue in SpringBoot

I am trying to use like operator in native query as below:
#Query(value = "SELECT max(id) FROM EMP where id like ___:country%", nativeQuery=true)
country as input parameter for method.
getting the below error:
java.lang.IllegalArgumentException: Unknown parameter name : country
can anyone please help me on this.
use JPA query in your repository interface like this:
#Query(nativeQuery = true, value = "SELECT max(r_id) FROM registrations WHERE r_name LIKE ?1")
int getCount (String name);
when you call this function add modulus(%) with your string like this:
#Autowired
private RegistrationRepository registrationRepository;
#GetMapping(value = "/reg-like")
#ResponseBody
public void regLike() {
int max = registrationRepository.getCount("s%");
System.out.println("" + max);
}
Or you can try something like this:
#Query(nativeQuery = true, value = "SELECT max(r_id) FROM registrations WHERE r_name LIKE %?1")
int getCount (String name);
Then you can omit the modulus(%), when calling the function.
See official documentation here.

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!

How to use JpaRepository operations (findOne) when having a native query with union all statment

I need to use JpaRepository with a query that has an union statement inside. Is it possible? This is my current implementation and the error I get so far:
Entity:
#Entity
public class PaymentDetails {
#Id
#Column
private String id;
#Enumerated(EnumType.STRING)
#Column(name = "rowtype")
private PaymentType paymentType;
#Embedded
private CardDetails cardDetails;
#Embedded
private BankAccountDetails bankAccountDetails;
Interface and query:
public interface PaymentsRepositoryManagerPayment1 extends JpaRepository<PaymentDetails,String> {
#Query(value = "select id,'CARD' as rowtype, cardnumber, cardtype, nameoncard, expirydate, startdate, securitycode, semafonecr, issuenumber, "
+ "null accountnumber, null accountholdername, null sortcode, null bic, null iban, null currency from pcs.BsbTempCardDetails "
+ "where id = :id union all select id, 'BANK' as rowtype, null cardnumber, null cardtype, null nameoncard, null expirydate, null startdate, "
+ "null securitycode, null semafonecr, null issuenumber, accountnumber, accountholdername, sortcode, bic, iban, currency "
+ "from pcs.BsbTempBankAccountDetails where id = :id", nativeQuery = true) <br>
List< PaymentDetails > findPaymentDetails(#Param("id") String id);
Call:
#Autowired private PaymentsRepositoryManagerPayment1 paymentsRepositoryManagerPayment1;
#Transactional(value = "paymentsRepositoryTransactionManager")
public PaymentDetails retrievePaymentDetailsById1(String id) {
return paymentsRepositoryManagerPayment1.findOne(id);
}
ERROR:
org.springframework.dao.InvalidDataAccessResourceUsageException: could not load an entity: [com.bskyb.repository.payments.model.PaymentDetails#cardId]; SQL [select paymentdet0_.id as id2_0_, paymentdet0_.accountholdername as accounth2_2_0_, paymentdet0_.accountnumber as accountn3_2_0_, paymentdet0_.bic as bic2_0_, paymentdet0_.currency as currency2_0_, paymentdet0_.iban as iban2_0_, paymentdet0_.sortcode as sortcode2_0_, paymentdet0_.cardnumber as cardnumber2_0_, paymentdet0_.cardtype as cardtype2_0_, paymentdet0_.expirydate as expirydate2_0_, paymentdet0_.issuenumber as issuenu11_2_0_, paymentdet0_.nameoncard as nameoncard2_0_, paymentdet0_.securitycode as securit13_2_0_, paymentdet0_.startdate as startdate2_0_, paymentdet0_.rowtype as rowtype2_0_ from PaymentDetails paymentdet0_ where paymentdet0_.id=?]; nested exception is org.hibernate.exception.SQLGrammarException: could not load an entity: [com.bskyb.repository.payments.model.PaymentDetails#cardId]
java.sql.SQLSyntaxErrorException: ORA-00942: table or view does not exist
All of what you showed doesn't seem to be too related to the exception you see:
The exception complaints about a table not being available. Make sure PaymentDetails exists when you issue the query (this is probably the reason you see the exception).
You call findOne(…). Thus the query declaration on findPaymentDetails(…) doesn't play into the use case at all.

Resources