Pagination on Native sql query gives : unexpected token: ON - java

While writing a pagination query in repository as below code segment, I'm getting following error.
Code
#Repository
public interface Aaaa extends PagingAndSortingRepository<TxnDealerInventoryItem, Long> {
#Query(value = "SELECT EM.PART_NO, EM.PART_NAME FROM TXN_DEALER_INVENTORY_ITEM E INNER JOIN MST_PRODUCT EM ON E.PRODUCT_ID = EM.PRODUCT_ID WHERE E.ACCOUNT_ID= :accountId AND EM.ALLOW_SERIAL_NUM = :isSerialized ORDER BY ?#{#pageable}",
countQuery = "SELECT COUNT(*) FROM TXN_DEALER_INVENTORY_ITEM E INNER JOIN MST_PRODUCT EM ON E.PRODUCT_ID = EM.PRODUCT_ID WHERE E.ACCOUNT_ID= :accountId AND EM.ALLOW_SERIAL_NUM = :isSerialized",
nativeQuery = true)
Page<Object[]> getNonSerializedDeviceList(#Param("accountId") Long accountId, #Param("isSerialized") String isSerialized, Pageable pageable);
}
Error
HQL: SELECT COUNT(*) FROM TXN_DEALER_INVENTORY_ITEM E INNER JOIN MST_PRODUCT EM ON E.PRODUCT_ID = EM.PRODUCT_ID WHERE E.ACCOUNT_ID= :accountId AND EM.ALLOW_SERIAL_NUM = :isSerialized
2023-02-10 18:52:52,753 ERROR [org.hibernate.hql.internal.ast.ErrorCounter] (http-/127.0.0.1:8881-1) line 1:76: unexpected token: ON
Native Query doesn't have any error when run from sql developer.
Framework versions are as follows, Unfortunately I'cant update these any further as there are limitations in deployment environment. You inputs are highly welcome on this !!
<spring.version>4.3.30.RELEASE</spring.version>
<spring.data.version>1.11.23.RELEASE</spring.data.version>
<hibernate.version>4.2.18.Final</hibernate.version>
PS : for testing purpose when I change the SQL to a very basic like a select *, It gives following error.
org.hibernate.hql.internal.ast.QuerySyntaxException: TXN_DEALER_INVENTORY_ITEM is not mapped [SELECT COUNT(*) FROM TXN_DEALER_INVENTORY_ITEM E ]

Got the problem resolved by removing the count query and make the return type to a List of object Array instead of Page as below code segment.
#Repository
public interface Aaaa extends PagingAndSortingRepository<TxnDealerInventoryItem, Long> {
#Query(value = "SELECT EM.PART_NO, EM.PART_NAME FROM TXN_DEALER_INVENTORY_ITEM E INNER JOIN MST_PRODUCT EM ON E.PRODUCT_ID = EM.PRODUCT_ID WHERE E.ACCOUNT_ID= :accountId AND EM.ALLOW_SERIAL_NUM = :isSerialized ORDER BY ?#{#pageable}",
nativeQuery = true)
List<Object[]> getNonSerializedDeviceList(#Param("accountId") Long accountId, #Param("isSerialized") String isSerialized, Pageable pageable);
}
Finally having good old Paginations :) Thanks everyone who looked into this.

Related

How select distinct value with pageable Spring data JPA?

I want to make a distinct select in my table with pagination, but it is claiming this error. Does anyone know how to solve it?
org.postgresql.util.PSQLException: ERROR: for SELECT DISTINCT, ORDER BY expressions must appear in select list
#Query(value = "SELECT DISTINCT budget.* FROM budget LEFT JOIN user_budget ON budget.id = user_budget.budget_id ORDER BY budget.created DESC, ?#{#pageable}",
countQuery = "SELECT DISTINCT count(*) FROM budget LEFT JOIN user_budget ON budget.id = user_budget.budget_id",
nativeQuery = true)
public Page<Budget> findAllByfilter(Pageable pageable);

Spring Data Native query pagination not working

I am using Spring Boot 2.0.2.RELEASE with Spring Data JPA. I am trying to do implement pagination with native query in MySql, my code is :
#Query(nativeQuery=true, value = "SELECT DISTINCT ud.latitude,ud.longitude,u.user_id userId FROM users u \n" +
"INNER JOIN user_devices ud ON u.id = ud.user_id\n" +
"WHERE ud.access_token IS NOT NULL AND ud.user_id <> 1\n" +
"ORDER BY calculateDistanceByLatLong(:userLat, :userLong, ud.latitude, ud.longitude) ASC \n#pageable\n",
countQuery = "SELECT COUNT(DISTINCT u.id) FROM users u \n" +
"INNER JOIN user_devices ud ON u.id = ud.user_id\n" +
"WHERE ud.access_token IS NOT NULL AND ud.user_id <> 1 \n#pageable\n")
public Page<LocationProjection> listNearByUsers(#Param("userLat")String userLatitude,#Param("userLong") String userLongitude, Pageable pageable) throws Exception;
I got the reference from this link.
And also reviewed this link.
But it's not adding any pagination code.
When I try to use :
userDao.listNearByUsers(userDeviceEntity.getLatitude(),userDeviceEntity.getLongitude(), PageRequest.of(pageNo, maxResults))
And for example set pageNo=0 and maxResults =1,all the results are displayed. So no pagination is being implemented.
I printed the fired query, it is :
SELECT
DISTINCT ud.latitude,
ud.longitude,
u.user_id userId
FROM
users u
INNER JOIN
user_devices ud
ON u.id = ud.user_id
WHERE
ud.access_token IS NOT NULL
AND ud.user_id <> 1
ORDER BY
calculateDistanceByLatLong(?,
?,
ud.latitude,
ud.longitude) ASC #pageable
And the count query as :
SELECT
COUNT(DISTINCT u.id)
FROM
users u
INNER JOIN
user_devices ud
ON u.id = ud.user_id
WHERE
ud.access_token IS NOT NULL
AND ud.user_id <> 1 #pageable
I thought Spring Data would add "LIMIT 0,1" in the main query but it's not working. The DAO interface is :
#Repository
public interface UserDao extends JpaRepository<UserEntity, Integer>{}
Please suggest some solution.
After going over several Q/A here, and none working properly (for my setup, at least). After reading this bit at Spring official documentation, I've managed to get it working by simply removing the "pageable" bit from my query.
I'm working with spring-boot version 2.0.1.RELEASE.
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);
}

CRudRepository native query could not return Result Set

I have a simple test query inside a CrudRepository interface that should return a List of entities.
public interface TestRepository extends CrudRepository<Test, TestId> {
#Query(value = "SELECT p FROM test p ", nativeQuery = true)
public List<Test> getTests();
}
When I test this I get the exception:
org.springframework.dao.InvalidDataAccessResourceUsageException: could
not extract ResultSet
If I don't use native query it works, but I want to use native query because I want to extend the select.
In order to make your query work :-
#Query(value = "SELECT * FROM TEST ", nativeQuery = true)
public List<Test> getTests();
The reason is simply because you are writing native query."SELECT p FROM test p" is not a native query
2 problems
in native SQL use native SQL :)
#Query(value = "SELECT p.* FROM test p ", nativeQuery = true)
your native Query returns an Object[] or a List of Object[].
You can change that, if you provide additional mapping information to the EntityManager.
By doing this you can tell the EntityManager to map the result into managed entities, scalar values of specific types or POJOs.
The simplest way to map the result of a native query into a managed entity is to select all properties of the entity and provide its as a parameter to the createNativeQuery method.
(sorry for using other examples)
Query q = em.createNativeQuery("SELECT a.id, a.version, a.firstname, a.lastname FROM Author a", Author.class);
List<Author> authors = q.getResultList();
All other mappings, like the following one which maps the query result into a POJO, need to be defined as SQLResultSetMappings.
#SqlResultSetMapping(
name = "AuthorValueMapping",
classes = #ConstructorResult(
targetClass = AuthorValue.class,
columns = {
#ColumnResult(name = "id", type = Long.class),
#ColumnResult(name = "firstname"),
#ColumnResult(name = "lastname"),
#ColumnResult(name = "numBooks", type = Long.class)}))
To use this mapping, you need to provide the name of the mapping as a parameter to the createNativeQuery method.
Query q = em.createNativeQuery("SELECT a.id, a.firstname, a.lastname, count(b.id) as numBooks FROM Author a JOIN BookAuthor ba on a.id = ba.authorid JOIN Book b ON b.id = ba.bookid GROUP BY a.id", "AuthorValueMapping");
List<AuthorValue> authors = q.getResultList();
#Query(value = "SELECT * FROM test p ", nativeQuery = true)

Delete From JPA Query with Inner Join

I keep getting errors when trying to get this SQL correct for a JPA repository delete. What is the correct syntax?
#Query("delete * from TS t inner join TSC c ON t.tenantId = c.id where t.id= ?1 AND c.endDate < ?2")
void deleteTSWithExpiredDate(Long id, Date date);
Caused by: java.lang.IllegalArgumentException: node to traverse cannot be null!
at org.hibernate.hql.internal.ast.util.NodeTraverser.traverseDepthFirst(NodeTraverser.java:63)
Another
#Query("delete t.* from TS t inner join TSC c ON t.tenantId = c.id where t.id= ?1 AND c.endDate < ?2")
void deleteTSWithExpiredDate(Long id, Date date);
expecting IDENT, found '*' near line 1, column 10 [delete t.*
Another
#Query("delete t from TS t inner join TSC c ON t.tenantId = c.id where t.id= ?1 AND c.endDate < ?2")
void deleteTSWithExpiredDate(Long id, Date date);
unexpected token: from near line 1, column 10 [delete t
The syntax for bulk delete is:
Query q = session.createQuery("delete Entity where id = :idParam");
// set params here
q.executeUpdate();
However from Hibernate documentation, no joins can be specified in a bulk HQL query. Sub-queries can be used in the where-clause, where the sub-queries themselves can contain joins.

Spring-Data FETCH JOIN with Paging is not working

I am trying to use HQL fetching my entity along with sub-entities using JOIN FETCH, this is working fine if I want all the results but it is not the case if I want a Page
My entity is
#Entity
#Data
public class VisitEntity {
#Id
#Audited
private long id;
.
.
.
#OneToMany(cascade = CascadeType.ALL,)
private List<VisitCommentEntity> comments;
}
and because I have millions of visits I need to use Pageable and I want to Fetch the comments in a single database query like :
#Query("SELECT v FROM VisitEntity v LEFT JOIN FETCH v.comments WHERE v.venue.id = :venueId and ..." )
public Page<VisitEntity> getVenueVisits(#Param("venueId") long venueId,...,
Pageable pageable);
That HQL call throws the following exception:
Caused by: java.lang.IllegalArgumentException: org.hibernate.QueryException: query specified join fetching, but the owner of the fetched association was not present in the select list [FromElement{explicit,not a collection join,fetch join,fetch non-lazy properties,classAlias=null,role=com.ro.lib.visit.entity.VisitEntity.comments,tableName=visitdb.visit_comment,tableAlias=comments1_,origin=visitdb.visit visitentit0_,columns={visitentit0_.visit_id ,className=com.ro.lib.visit.entity.VisitCommentEntity}}] [select count(v) FROM com.ro.lib.visit.entity.VisitEntity v LEFT JOIN FETCH v.comments WHERE v.venue.id = :venueId and (v.actualArrival > :date or v.arrival > :date)]
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1374)
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1310)
at org.hibernate.ejb.AbstractEntityManagerImpl.createQuery(AbstractEntityManagerImpl.java:309)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
and once I remove the paging everything works fine
#Query("SELECT v FROM VisitEntity v LEFT JOIN FETCH v.comments WHERE v.venue.id = :venueId and ..." )
public List<VisitEntity> getVenueVisits(#Param("venueId") long venueId,...);
Obviously the problem is the count query from Spring-Data, but how can we fix it?
The easiest way is to use the countQuery attribute of the the #Query annotation to provide a custom query to be used.
#Query(value = "SELECT v FROM VisitEntity v LEFT JOIN FETCH v.comments …",
countQuery = "select count(v) from VisitEntity v where …")
List<VisitEntity> getVenueVisits(#Param("venueId") long venueId, …);
Alternatively in newest versions of Spring (supporting JPA 2.1 specification) you can use entity graph like this:
#EntityGraph(attributePaths = "roles")
#Query("FROM User user")
Page<User> findAllWithRoles(Pageable pageable);
Of course named entity graphs work as well.
You have to specify countQuery param for #Query and now you can use Page or List as return value.
#Query(value = "SELECT v FROM VisitEntity v LEFT JOIN FETCH v.comments WHERE v.venue.id = :venueId and ...",
countQuery = "SELECT count(v) FROM VisitEntity v LEFT JOIN v.comments WHERE v.venue.id = :venueId and ..." )
public Page<VisitEntity> getVenueVisits(#Param("venueId") long venueId,...,
Pageable pageable);
If you want completely control your query build by Specification with join fetch you can check CriteriaQuery return type and change join fetch logic according to query type like this:
public class ContactSpecification implements Specification<Contact> {
#Override
public Predicate toPredicate(Root<Contact> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
if(query.getResultType() == Long.class) {
root.join(Contact_.company);
} else {
root.fetch(Contact_.company);
}
return cb.equal(root.get(Contact_.company).get(Company_.name), "Company 123");
}
}
I was not able to find this info in documentation, but from SimpleJpaRepository.getCountQuery() method you can see query for count request first build for Long return type, and later fetch for expected class is running.
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Long> query = builder.createQuery(Long.class);
Root<S> root = applySpecificationToCriteria(spec, domainClass, query);
It can be not reliable since it is an implementation details which can be changed, but it works.
Try countProjection
#Query(value="SELECT v FROM VisitEntity v LEFT JOIN FETCH v.comments WHERE v.venue.id = :venueId and ..." ,
countProjection = "v.id")
public Page<VisitEntity> getVenueVisits(#Param("venueId") long venueId,...,
Pageable pageable);

Categories