How to paginate custom queries with group by? - java

I have the following JPQL query being mapped to a custom projection. The query is working correctly until I set the size attribute at the pagination object.
#Query(value = "SELECT EXTRACT(WEEK FROM e.time) AS week "
+ "COUNT(r.id) AS total FROM MyEntity e "
+ "LEFT JOIN e.relationship r GROUP BY week")
Page<WeeklyReport> findWeeklyReport(Pageable pageable);
Hibernate fails with the error java.sql.SQLSyntaxErrorException: Unknown column 'week' in 'group statement' after running the following query:
select count(entity0_.id) as col_0_0_ from entity entity0_
left outer join relationship relationship1_ on entity0_.id=relationship1_.tide_id
group by week
How can I have the same query without my group name break the pagination above?
The error does not happen if the size is not defined.

Related

pagination and naive query + eclipse link

I am trying to write a native query and pagination in eclipse link + JPA for PostgreSQL
but I'm getting exceptions :
Here is the code what I tried :
#Query(nativeQuery = true, value= "select a.* from rqst a LEFT OUTER JOIN table_b b ON a.id= b.id where a.code='abc' ORDER BY /*#pagable*/")
#Query(nativeQuery = true, value= "select a.* from rqst a LEFT OUTER JOIN table_b b ON a.id= b.id where a.code='abc' ORDER BY ?#{#pageable}")
and I followed this link but no luck :
Spring Data and Native Query with pagination
any help appreciated
when I use second query I got this exception :
Can't infer the SQL type to use for an instance of org.springframework.data.domain.PageRequest. Use setObject() with an explicit Types value to specify the type to use.
at org.postgresql.jdbc.PgPreparedStatement.setObject(PgPreparedStatement.java:1039) ~[postgresql-9.4.1208.jar:9.4.1208]

How to map result got from native query in hibernate?

I have a query that has more columns then what my entity class has.
In order to not let hibernate complaints, I have to add an annotation to the field like
#Transient
private Integer count;
But by doing this makes hibernate not able to map count. Let's say my query is
session.createSQLQuery("SELECT p.*, count(p.id), sqrt(1+2) as distance FROM post p group by p.id")
I know the query doesn't make any logical sense. This is just for example. The columns return from query above will have everything in post and two extra columns, count and distance. I wanted to map the result to my entity with count and distance are annotated with #Transient, or if there's a better way to map the result. I'm more than happy to do so. The goal is not to do this in an entity but a class with mapped result. I've tried calling addEntity() but doesn't seem to help.
You can use Result Set Transformer to achieve this.
Step 1 ) Create a new DTO class with all the fields that you query going to return
Step 2 ) Add the below line
setResultTransformer( Transformers.aliasToBean(DTO.class))
Example :
List resultWithAliasedBean = session.createQuery(
"SELECT p.*, count(p.id), sqrt(1+2) as distance FROM post p group by p.id")
.setResultTransformer(Transformers.aliasToBean(DTO.class))
.list();
DTO dto = (DTO) resultWithAliasedBean.get(0);
Note : Make sure the field names in the DTO class match the column name which your query is returning.
I see that you are using Hibernate so Yathish answer works fine.
But if you want to do it with JPA spec then you can use Result Set Mapping
Query q = em.createNativeQuery(
"SELECT c.id, c.name, COUNT(o) as orderCount, AVG(o.price) AS avgOrder " +
"FROM Customer c " +
"JOIN Orders o ON o.cid = c.id " +
"GROUP BY c.id, c.name",
"CustomerDetailsResult");
#SqlResultSetMapping(name="CustomerDetailsResult",
classes={
#ConstructorResult(targetClass=com.acme.CustomerDetails.class,
columns={
#ColumnResult(name="id"),
#ColumnResult(name="name"),
#ColumnResult(name="orderCount"),
#ColumnResult(name="avgOrder", type=Double.class)})
})
There you have to specifiy the mappin of the columns from the SQL result set to the DTO.
And if you think this is to complicated there is a open source project called QLRM (Query Lanaguage Result Mapper) that mapps any SQL statement to a POJO.
http://simasch.github.io/qlrm/
And last but not least if you will do extensive SQL processing why not have a look at jOOQ: https://www.jooq.org/

Spring data jpa left join fetch and where clause

I have the following query in my repository:
SELECT p FROM Project p JOIN p.users u WHERE u.login =:login
There is a Many To Many relationshio between user and project.
Everything works fine and it returns the user's projects, but I want it for each project to return the corresponding set of users.
UPDATE: Tried the following as mateuszlo suggested:
SELECT p FROM Project p JOIN FECTH p.users JOIN p.users u WHERE u.login =:login
But now i got the following exception:
nested exception is java.lang.IllegalArgumentException: Count query validation failed for method public abstract org.springframework.data.domain.Page com.example.app.repository.ProjectRepository.findAllByUserLo‌​gin(java.lang.String‌​,org.springframework‌​.data.domain.Pageabl‌​e)! org.hibernate.QueryException: query specified join fetching, but the owner of the fetched association was not present in the select list
It is impossible to create a named alias for a FETCH JOIN query to use it in a WHERE statement. JPA doesn't allow this on purpose, because it could easly lead to unwanted situations.
Consider a Project X, which has 3 Users: John, Tom and Mike. Query with FETCH JOIN for projects, that have user John would return Project X with only one User - John. That would produce an incomplete Project entity, which is inconsistent with current database state.
So what you have to do is to join twice. First time with normal JOIN, to identify proper Projet records, and then second time with FETCH JOIN to fetch correspoding Users:
SELECT p FROM Project p
JOIN FETCH p.users
JOIN p.users u WHERE u.login =:login
First off I'm assuming you're using pagination? That is you have a Pagable parameter on your JPA query method? If so, there are two ways to solve this.
Count query
#Query(value = "SELECT p FROM Project p " +
"LEFT JOIN FETCH p.users u WHERE u.login = :login",
countQuery = "SELECT COUNT(u) FROM User u WHERE u.login = :login")
Page<Project> fetchProjectsByUserLogin(String login, Pageable pageable);
Entity Graph
#Query(value = "SELECT p FROM Project p " +
"LEFT JOIN p.users u WHERE u.login = :login")
#EntityGraph(attributePaths = {"users"}, type = EntityGraph.EntityGraphType.FETCH)
Page<Project> fetchProjectsByUserLogin(String login, Pageable pageable);

HQL, can I parameterize the FROM clause?

I have this HQL query:
Query q = em.createQuery (
"DELETE FROM Annotation a WHERE a.id IN ( " +
" SELECT ja.id FROM :entityName an JOIN an.annotations ja)"
);
and I'm being told: QuerySyntaxException: unexpected token: : near line 1
Do I have any hope of making the entity name after FROM a parameter? I have a list of entities to send to this query and I'm afraid that string concatenation is too slow.
You can't substitute the Entity name the parameters work for entity properties not instead.
You could select the entities ids to be deleted with one query and then pass them to a second delete query, but for READ_COMMITED transaction isolation you might still end up with someone else inserting one child entity that would have matched your query. SERIALIZABLE will solve this issue.

Hibernate: Find only those rows that aren't in a many-to-many relationship

OK, I have mapped with annotations two tables with a bidirectional #ManyToMany relationship.
Now I want to return only the elements which aren't in a many to many relationship, and I'm trying to use the code from here, but It throws an exception at runtime.
Here's the HQL:
String hql = "select a from Article a " +
"left join a.tags t " +
"group by a " +
"having count(t)=0";
Is there a better way to return those elements? Or to fix the error in this query?
The exception it throws now is:
column "article0_.id" must appear in the GROUP BY clause or be used in an aggregate function
org.springframework.dao.InvalidDataAccessResourceUsageException: could not execute query; SQL ...
The query is correct, but on some databases group by a is not enough, you have to enumarate all properties of a, such as group by a.id, a.title.
Alternatively, you can use the following query:
select a from Article a where a.tags is empty
See also:
16.12. The group by clause
Not sure if it would work, but you can give it a try:
String hql = "select a from Article a " +
"where a.tags=null ";

Categories