What is the LIMIT clause alternative in JPQL? - java

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?

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.

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

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

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

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

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.

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.

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

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

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.

Related

Spring JPA. How to map from #Query(nativeQuery = true) to a POJO [duplicate]

This question already has answers here:
JPA : How to convert a native query result set to POJO class collection
(22 answers)
Closed 2 years ago.
Have a query, let it be
select 1 "colName"
I want to map the result to a POJO type using Spring Data JPA.
Thus the picture is:
public interface MyAwesomeSuperInterface extends CrudRepository {
#Query(value = "select 1 \"colName\"", nativeQuery = true)
List<POJO> something();
}
And the question is HOW to map it to the POJO.class?
Following the common suggestions I assume I'll get:
No, I don't want to change it to JSQL and do a 'new POJO'.
Why? Because I have a complex sql query, which isn't reflectable to JSQL.
No, I will not bring up the query. I merely want to know how to map the upper example to a POJO using Spring Data. Thank you
You can use DTO projection with native queries:
// Projection Interface
public interface UserProjection {
String getName();
String getEmail();
Integer getId();
String getComment();
}
public interface UserRepository extends CrudRepository<User, Integer> {
#Query(value = "select u.name, u.email, c.comment from User u join
Comment c on u.id = c.user_id where u.id in :ids", nativeQuery = true)
List<UserProjection> getUserInterface(List<Integer> ids);
}
This is one example I recently tried with DTO projections. This will Simply map result of the native query to UserProjection.
For more information read: Spring Data JPA Projection support for native queries
It should auto-convert from sql query to pojo you need to define the correct datatype in below example I am using List<User> as the query will return all the data from the table :
#Query("select * from User u")
List<User> findUsers();
If you specify columns then you need to specify constructor in pojo which accepts the same fields.
You can also use parameterized query :
#Query("select * from User u where u.user_id =: userId")
List<User> findUserById(#Param("userId") String userId);
You can also refer to this document :
https://www.baeldung.com/spring-data-jpa-query
it will work hoping for good.

How to find entities by spring data with same values of different fields?

I look for short (one-method-long) solution of my problem.
I want to write query in spring data that does the same as:
select * from Users
where city = #EnteredData data OR homeTown = #EnteredData
I tried
List<User> findDistinctCity_findByHomeTown(String city);
I have no ideas how to find users whose city or homeTown is equal to given value in one method.
I will go with the below version. I better write my own queries instead of trying to use the Spring Data generated DSL one
#Query("select u from User u where u.city = :city or u.homeTown = :city") public List<User> getUserInfo(#Param("city") String city);
U can use method query, Add Or in query
List<User> findByCityOrHometowm(String name);
JpqL snippet will create as
where x.city= ?1 or x.hometown = ?2

Using JPA native query with zero or more parameters

Goal: I am trying to build query via JPA which returns a list of locations, nearest to the coordinates passed to the query. The ability to apply filter(s), sort and paginate on the query are required. The following native SQL query is what I came come up with so far:
SELECT s.*
,sqrt(power(abs(:latitude-g.latitude),2)+power(abs(:longitude-g.longitude),2)) as d
FROM geolocations g
INNER JOIN salons s
ON g.zip=s.zip
WHERE LOWER(s.salon_name) LIKE LOWER(CONCAT('%',:q,'%'))
ORDER BY d asc limit
:pageSize offset :offset
Paganation, some sorting, and location based search are all working, but I cannot get filtering to work.
The filter requirement states that a user must also be able to filter search results(0 or more filters) by fields, like owner,state,zip... I want to use something like querydsl or the JPA Specification object to build a small query language but I cannot build out a working implementation given the fact that I am using a native query. I am looking for any recommendations on how I could implement the field filters into my existing repository.
Here is my entire repository:
public interface SalonRepository extends JpaRepository<Salon,String>, JpaSpecificationExecutor<Salon>{
#Query("select s from Salon s where s.city = :city and s.state = :state")
Page<Salon> findByCity(#Param("state")String state,#Param("city")String city,Pageable pageable);
#Query("select s from Salon s where s.salonName = :name")
Page<Salon> findByName(#Param("name")String name,Pageable pageable);
//THIS IS THE ONE I WANT TO ADD THE USER DEFINED FILTERS TO
#Query(value = "select s.*,sqrt(power(abs(:latitude-g.latitude),2)+power(abs(:longitude-g.longitude),2)) as d from geolocations g inner join salons s on g.zip=s.zip where LOWER(s.salon_name) like LOWER(CONCAT('%',:q,'%')) order by d asc limit :pageSize offset :offset", nativeQuery = true)
List<Salon> findNearest(#Param("latitude")float latitude,#Param("longitude")float longitude,#Param("q")String query,#Param("pageSize")int pageSize,#Param("offset") int offset);
#Query(value = "select sqrt(power(abs(:latitude-g.latitude),2)+power(abs(:longitude-g.longitude),2)) as d from geolocations g inner join salons s on g.zip=s.zip where LOWER(s.salon_name) like LOWER(CONCAT('%',:q,'%')) order by d asc", nativeQuery = true)
List<Float> findNearestInt(#Param("latitude")float latitude,#Param("longitude")float longitude,#Param("q")String query);
#Query("select s from Salon s where lower(s.salonName) like LOWER(CONCAT('%',:salonName,'%'))")
Page<Salon> findAll(#Param("salonName")String salonName,Pageable pageable);
}
After hours of searching, I am fairly certain I am on the wrong track given the lack of information I've uncovered.
Any and all help is appreciated!

Spring Data and Native Query with Sorting

In a web project, using spring-data(1.10.4.RELEASE) with a Oracle database, i am trying use a native query with a Sort variable.
public interface UserRepository extends JpaRepository<User, Long> {
#Query(nativeQuery = true,value = "SELECT * FROM USERS WHERE LASTNAME = :lastname #sort")
List<User> findByLastname(#Param("lastname") String lastname, Sort sort);
}
The query launched is:
SELECT * FROM USERS WHERE LASTNAME = 'Lorite' #sort ORDER BY LASTNAME
Like you can see the annotation "#sort" is still there.
I have tried Spring Data and Native Query with pagination but the annotation is there yet and using another syntax like ?#{#sort} or {#sort} the problem persist.
Anything is welcome.
Thanks!
The documentation says:
Note, that we currently don’t support execution of dynamic sorting for native queries as we’d have to manipulate the actual query declared and we cannot do this reliably for native SQL.
Furthermore, this #sort interpolation does not exist
[1] http://docs.spring.io/spring-data/jpa/docs/current/reference/html/
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);
}
Example 64. Declare native count queries for pagination at the query method by using #Query
Native Queries with Spring Data JPA

createNativeQuery will not return rows in java but will in db2 sql [duplicate]

I know I can pass a list to named query in JPA, but how about NamedNativeQuery? I have tried many ways but still can't just pass the list to a NamedNativeQuery. Anyone know how to pass a list to the in clause in NamedNativeQuery? Thank you very much!
The NamedNativeQuery is as below:
#NamedNativeQuery(
name="User.findByUserIdList",
query="select u.user_id, u.dob, u.name, u.sex, u.address from user u "+
"where u.user_id in (?userIdList)"
)
and it is called like this:
List<Object[]> userList = em.createNamedQuery("User.findByUserIdList").setParameter("userIdList", list).getResultList();
However the result is not as I expected.
System.out.println(userList.size()); //output 1
Object[] user = userList.get(0);
System.out.println(user.length); //expected 5 but result is 3
System.out.println(user[0]); //output MDAVERSION which is not a user_id
System.out.println(user[1]); //output 5
System.out.println(user[2]); //output 7
The above accepted answer is not correct and led me off track for many days !!
JPA and Hibernate both accept collections in native query using Query.
You just need to do
String nativeQuery = "Select * from A where name in :names"; //use (:names) for older versions of hibernate
Query q = em.createNativeQuery(nativeQuery);
q.setParameter("names", l);
Also refer the answers here which suggest the same (I picked the above example from one of them)
Reference 1
Reference 2 which mentioned which cases paranthesis works which giving the list as a parameter
*note that these references are about jpql queries, nevertheless the usage of collections is working with native queries too.
A list is not a valid parameter for a native SQL query, as it cannot be bound in JDBC. You need to have a parameter for each argument in the list.
where u.user_id in (?id1, ?id2)
This is supported through JPQL, but not SQL, so you could use JPQL instead of a native query.
Some JPA providers may support this, so you may want to log a bug with your provider.
Depending on your database/provider/driver/etc., you can, in fact, pass a list in as a bound parameter to a JPA native query.
For example, with Postgres and EclipseLink, the following works (returning true), demonstrating multidimensional arrays and how to get an array of double precision. (Do SELECT pg_type.* FROM pg_catalog.pg_type for other types; probably the ones with _, but strip it off before using it.)
Array test = entityManager.unwrap(Connection.class).createArrayOf("float8", new Double[][] { { 1.0, 2.5 }, { 4.1, 5.0 } });
Object result = entityManager.createNativeQuery("SELECT ARRAY[[CAST(1.0 as double precision), 2.5],[4.1, 5.0]] = ?").setParameter(1, test).getSingleResult();
The cast is there so the literal array is of doubles rather than numeric.
More to the point of the question - I don't know how or if you can do named queries; I think it depends, maybe. But I think following would work for the Array stuff.
Array list = entityManager.unwrap(Connection.class).createArrayOf("int8", arrayOfUserIds);
List<Object[]> userList = entityManager.createNativeQuery("select u.* from user u "+
"where u.user_id = ANY(?)")
.setParameter(1, list)
.getResultList();
I don't have the same schema as OP, so I haven't checked this exactly, but I think it should work - again, at least on Postgres & EclipseLink.
Also, the key was found in: http://tonaconsulting.com/postgres-and-multi-dimensions-arrays-in-jdbc/
Using hibernate, JPA 2.1 and deltaspike data I could pass a list as parameter in query that contains IN clause. my query is below.
#Query(value = "SELECT DISTINCT r.* FROM EVENT AS r JOIN EVENT AS t on r.COR_UUID = t.COR_UUID where " +
"r.eventType='Creation' and t.eventType = 'Reception' and r.EVENT_UUID in ?1", isNative = true)
public List<EventT> findDeliveredCreatedEvents(List<String> eventIds);
can be as simple as:
#Query(nativeQuery =true,value = "SELECT * FROM Employee as e WHERE e.employeeName IN (:names)")
List<Employee> findByEmployeeName(#Param("names") List<String> names);
currently I use JPA 2.1 with Hibernate
I also use IN condition with native query. Example of my query
SELECT ... WHERE table_name.id IN (?1)
I noticed that it's impossible to pass String like "id_1, id_2, id_3" because of limitations described by James
But when you use jpa 2.1 + hibernate it's possible to pass List of string values. For my case next code is valid:
List<String> idList = new ArrayList<>();
idList.add("344710");
idList.add("574477");
idList.add("508290");
query.setParameter(1, idList);
In my case ( EclipseLink , PostGreSQL ) this works :
ServerSession serverSession = this.entityManager.unwrap(ServerSession.class);
Accessor accessor = serverSession.getAccessor();
accessor.reestablishConnection(serverSession);
BigDecimal result;
try {
Array jiraIssues = accessor.getConnection().createArrayOf("numeric", mandayWorkLogQueryModel.getJiraIssues().toArray());
Query nativeQuery = this.entityManager.createNativeQuery(projectMandayWorkLogQueryProvider.provide(mandayWorkLogQueryModel));
nativeQuery.setParameter(1,mandayWorkLogQueryModel.getPsymbol());
nativeQuery.setParameter(2,jiraIssues);
nativeQuery.setParameter(3,mandayWorkLogQueryModel.getFrom());
nativeQuery.setParameter(4,mandayWorkLogQueryModel.getTo());
result = (BigDecimal) nativeQuery.getSingleResult();
} catch (Exception e) {
throw new DataAccessException(e);
}
return result;
Also in query cannot use IN(?) because you will get error like :
Caused by: org.postgresql.util.PSQLException: ERROR: operator does not exist: numeric = numeric[]
'IN(?)' must be swapped to '= ANY(?)'
My solution was based on Erhannis concept.
In jpa, it worked for me
#Query(nativeQuery =true,value = "SELECT * FROM Employee as e WHERE e.employeeName IN (:names)")
List<Employee> findByEmployeeName(#Param("names") List<String> names);
Tried in JPA2 with Hibernate as provider and it seems hibernate does support taking in a list for "IN" and it works. (At least for named queries and I believe it will be similar with named NATIVE queries)
What hibernate does internally is generate dynamic parameters, inside the IN same as the number of elements in the passed in list.
So in you example above
List<Object[]> userList = em.createNamedQuery("User.findByUserIdList").setParameter("userIdList", list).getResultList();
If list has 2 elements the query will look like
select u.user_id, u.dob, u.name, u.sex, u.address from user u "+
"where u.user_id in (?, ?)
and if it has 3 elements it looks like
select u.user_id, u.dob, u.name, u.sex, u.address from user u "+
"where u.user_id in (?, ?, ?)
you should do this:
String userIds ="1,2,3,4,5";
List<String> userIdList= Stream.of(userIds.split(",")).collect(Collectors.toList());
Then, passes like parameter inside your query, like this:
#NamedNativeQuery(name="User.findByUserIdList", query="select u.user_id, u.dob, u.name, u.sex, u.address from user u where u.user_id in (?userIdList)")
It's not possible with standard JPA. Hibernate offers the proprietary method setParameterList(), but it only works with Hibernate sessions and is not available in JPA's EntityManager.
I came up with the following workaround for Hibernate, which is not ideal but almost standard JPA code and has some nice properties to it.
For starters you can keep the named native query nicely separated in a orm.xml file:
<named-native-query name="Item.FIND_BY_COLORS" result-class="com.example.Item">
<query>
SELECT i.*
FROM item i
WHERE i.color IN ('blue',':colors')
AND i.shape = :shape
</query>
</named-native-query>
The placeholder is wrapped in single quotes, so it's a valid native JPA query. It runs without setting a parameter list and would still return correct results when other matching color parameters are set around it.
Set the parameter list in your DAO or repository class:
#SuppressWarnings("unchecked")
public List<Item> findByColors(List<String> colors) {
String sql = getQueryString(Item.FIND_BY_COLORS, Item.class);
sql = setParameterList(sql, "colors", colors);
return entityManager
.createNativeQuery(sql, Item.class)
.setParameter("shape", 'BOX')
.getResultList();
}
No manual construction of query strings. You can set any other parameter as you normally would.
Helper methods:
String setParameterList(String sql, String name, Collection<String> values) {
return sql.replaceFirst(":" + name, String.join("','", values));
}
String getQueryString(String queryName, Class<?> resultClass) {
return entityManager
.createNamedQuery(queryName, resultClass)
.unwrap(org.hibernate.query.Query.class) // Provider specific
.getQueryString();
}
So basically we're reading a query string from orm.xml, manually set a parameter list and then create the native JPA query. Unfortunately, createNativeQuery().getResultList() returns an untyped query and untyped list even though we passed a result class to it. Hence the #SuppressWarnings("unchecked").
Downside: Unwrapping a query without executing it may be more complicated or impossible for JPA providers other than Hibernate. For example, the following might work for EclipseLink (untested, taken from Can I get the SQL string from a JPA query object?):
Session session = em.unwrap(JpaEntityManager.class).getActiveSession();
DatabaseQuery databaseQuery = query.unwrap(EJBQueryImpl.class).getDatabaseQuery();
databaseQuery.prepareCall(session, new DatabaseRecord());
Record r = databaseQuery.getTranslationRow();
String bound = databaseQuery.getTranslatedSQLString(session, r);
String sqlString = databaseQuery.getSQLString();
An alternative might be to store the query in a text file and add code to read it from there.
You can pass in a list as a parameter, but:
if you create a #NamedNativeQuery and use .createNamedQuery(), you don't use named param, you used ?1(positional parameter). It starts with 1, not 0.
if you use .createNativeQuery(String), you can use named param.
You can try this :userIdList instead of (?userIdList)
#NamedNativeQuery(
name="User.findByUserIdList",
query="select u.user_id, u.dob, u.name, u.sex, u.address from user u "+
"where u.user_id in :userIdList"
)

Categories