Get distinct column values with a Spring Data JpaRepository - spring

I have this entity (annotations omitted for brevity):
#Entity
class Product {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Lob
private String category;
#Lob
private String name;
#Lob
private String customer_number;
// more attributes omitted
}
I have to get a list of distinct category value for a particular uid
In my JpaRepository I have this:
#Query("SELECT DISTINCT product.category FROM Product as product WHERE product.customerNumber = :cn ORDER BY product.category")
Page<String> findDistinctCategoryByCustomerNumber(String cn,
Pageable pageable);
Without the #Query annotation, the ids are returned, instead of the category values. The generated SQL looks like this:
select distinct product0_.id as id1_0_, product0_.customer_number as customer2_0_, product0_.category as directory3_0_, product0_.name as name4_0_ from product product0_
where product0_.customer_number=?
order by product0_.id desc limit ?
But I need the distinct categories not the product entities. Short of another idea I added the #Query annotation above. But now I get this error:
Order by expression "PRODUCT0_.ID" must be in the result list in this case; SQL statement:
select distinct product0_.directory as col_0_0_ from product product0_ where product0_.customer_number=? order by product0_.directory, product0_.id desc limit ? [90068-197]
But I cannot add id to the result list because that would make the DISTINCT useless, as id is the primary key.
So I either need a suitable method name for automatic query generation or a way to stop JpaRepository from adding its order by clause to the end of my #Query.

The unwanted order by gets created since you are requesting a paged result. The notion of the n-th page only makes sense when your results are ordered and the ordering is coming from your Pageable method argument.
Remove the order by from your query and set the sort attribute of your Pageable to sort by category.

Related

Select only the ids using Specification and Pageable

I am looking to select only the ids using this approach. Not sure how to get only the ids of matching employees.
Page<Employee> findAll(Specification<Employee> specification, Pageable pageable);

Spring MVC, Select Special Columns in Native SELECT Query

this is my native SELECT Query in Repository
#Modifying
#Query(value = "SELECT * FROM tasks WHERE title LIKE '%Java%' ORDER BY id DESC ", nativeQuery = true)
List<Task> listAllTasks();
this works ok, but when I use custom column name instead of *, like this
#Modifying
#Query(value = "SELECT title FROM tasks WHERE title LIKE '%Java%' ORDER BY id DESC ", nativeQuery = true)
List<Task> listAllTasks();
I have this error :
org.postgresql.util.PSQLException: The column name id was not found in this ResultSet.
any Help?
The resultset doesn't have the "id" in it, you have to provide it.
You should change the way you are declaring your SQL:
SELECT t.title, t.id FROM tasks t WHERE t.title LIKE '%Java%' ORDER BY t.id DESC
Check out this sort example:Native Queries
Select * from Entity -> returns a List of Entity
Example:
#Query(select * from tasks)
List<Task> findAllTasks();
Select column from Entity -> returns a List of Types of the entity.
Example:
#Query(select t.title from tasks t)
List<String> findTitle_AllTasks();
title is of the type String
Select multiple columns from Entity -> returns an Object[] holding the data
Example:
#Query(select t.id, t.title from tasks t)
List<Object[]> findIdTitle_AllTasks();
So, you are retrieving String type data - title and asking to return a List of Task type. This is causing the problem. You can actually check the hibernate docs under HQL and JPQL to understand this.
Plus, you are doing a SELECT (DQL operation). #Modifying is rudimentary here as it is used for DML operations using Data JPA - UPDATE/DELETE.

Springboot jpa, get rows with specific fields using two joins

I have entities :Student, Class, Room.
student M-M class, student M-M rooms, thus two junction tables. Now I want to get all students assigned to class X and room Y. Thus I've made:
#Query("SELECT s from Student s INNER JOIN s.classes c INNER JOIN s.rooms r WHERE c.id LIKE ?1 AND r.id LIKE ?2")
Page<Student> findAllInClassAndRoom(final Long classId, final Long roomId, final Pageable pageable);
But it gives me wrong results. Is there an error in my query ?
The only error in your query is the LIKE statement. Just change the equal sign "=". As below:
#Query("SELECT s from Student s INNER JOIN s.classes c INNER JOIN s.rooms r WHERE c.id = ?1 AND r.id = ?2")
Page<Student> findAllInClassAndRoom(final Long classId, final Long roomId, final Pageable pageable);
The LIKE statement allows a greater mass of data. Because it would allow any Room or Class that part of the code is the id entered as a parameter.
LIKE statement specification

Order By Date Desc Limit in Spring Data JPA

I am trying to limit the query results by using Limit query. With out limit the query is working as expected.
#Query("SELECT a FROM DrmAdpodTimeSlot a where a.startTime > :startTime order by a.startTime desc")
public List<DrmAdpodTimeSlot> findByStartTime(#Param("startTime") Timestamp startTime);
But When I try to limit the records by using limit (no.of records), as follows,
#Query("SELECT a FROM DrmAdpodTimeSlot a where a.startTime > :startTime order by a.startTime desc limit 2")
public List<DrmAdpodTimeSlot> findByStartTime(#Param("startTime") Timestamp startTime);
From the above query I am getting the following error,
Caused by: org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected token: limit near line 1, column 110 [SELECT a FROM com.dooreme.domain.DrmAd
podTimeSlot a where a.startTime > :startTime order by a.startTime desc limit 2]
How can I use the order by limit query in spring data jpa query?
You can not add pagination support to the Query annotation. There is no need for adding sorting and pagination functionality into HQL/JPQL when you're using Spring Data JPA. Use Pageable as the second argument instead, like following:
#Query("SELECT a FROM DrmAdpodTimeSlot a where a.startTime > :startTime")
public List<DrmAdpodTimeSlot> findByStartTime(#Param("startTime") Timestamp startTime, Pageable pageable);
Pageable encasulates the sort and paging functionality, as spring data jpa doc says:
Add Pageable instance to the query method to dynamically add paging to
your statically defined query. A Page knows about the total number of
elements and pages available. It does so by the infrastructure
triggering a count query to calculate the overall number. As this
might be expensive depending on the store used, Slice can be used as
return instead. A Slice only knows about whether there’s a next Slice
available which might be just sufficient when walking thought a larger
result set.
So, you can use either:
#Query("SELECT a FROM DrmAdpodTimeSlot a where a.startTime > :startTime")
public Page<DrmAdpodTimeSlot> findByStartTime(#Param("startTime") Timestamp startTime, Pageable pageable);
Or:
#Query("SELECT a FROM DrmAdpodTimeSlot a where a.startTime > :startTime")
public Slice<DrmAdpodTimeSlot> findByStartTime(#Param("startTime") Timestamp startTime, Pageable pageable);
Also:
Sorting options are handled through the Pageable instance too.
None of the other answers really answered your question of how to limit to the top 2 start times descending order. I'm not sure how to do it using jpql, but using jpa query, you can do findTop2ByOrderByStartTimeDesc
Also, see this post
How would I write SELECT TOP 25 sql query in Spring data repository
You can use in Repository your query:
#Query("SELECT a FROM DrmAdpodTimeSlot a WHERE a.startTime>'?1' ORDER BY a.startTime DESC")
List<DrmAdpodTimeSlot> findByStartTime(String startTime, Pageable pageable);
}
And in the Service, use a PageRequest, returning a Page object:
Page<DrmAdpodTimeSlot> top2 = arcustRepository.findByStartTime(arcustno, PageRequest.of(0, 2));
List<DrmAdpodTimeSlot> top2StartTimes = top2.getContent();

HQL Select New doesn't return row when a foreign key is null

I have the following named query
#NamedQuery(name = "UserFlight.getUserFlightDetails",
query = "SELECT new com.foobar.UserFlightDetails(uf.flight.divertedAirport, uf.flight.number) " +
"FROM UserFlight uf WHERE uf.user.id=?1 AND uf.flight.id=?2")
The UserFlightDetails constructor is as follows
public UserFlightDetails(Airport airport, String flightNumber) {
this.setDivertedAirport(airport);
this.setFlightNumber(flightNumber);
}
divertedAirport is a foreign key in the flight table, path=(uf.flight.divertedAirport)
My problem is when divertedAirport is null (it's a nullable foreign key), my HQL query returns null as the result (The code doesn't even trigger the constructor above), so I don't get the flightNumber which is never null.
If the divertedAirport isn't null, I get both the airport and the flight number fine (and the above constructor gets executed just fine).
What could be causing this and how could I resolve it? I tried some null functions like nullif and coalesce but nothing helped.
I'm using spring boot 1.2.7, hibernate-core 4.3.11.Final
Probably, the problem is the uf.flight.divertedAirport. This expression do a JOIN between flight and divertedAirport but, as you say, divertedAirport is a fk and can be null.
So, you need to use the LEFT JOIN.
I would rewrite your query like this:
#NamedQuery(name = "UserFlight.getUserFlightDetails",
query =
"SELECT new com.foobar.UserFlightDetails(divertedAirport, flight.number)
FROM UserFlight uf
JOIN uf.flight flight
LEFT JOIN flight.divertedAirport divertedAirport
JOIN uf.user user
WHERE user.id = ?1 AND flight.id = ?2 ")
I remove the references like uf.user.id for a explicit JOIN (JOIN uf.user user plus user.id), because is more legible and this kind of problem that generated your question is more easy to find using this way to write JPQL queries.

Resources