GroupBy query with Querydsl and Spring Boot - spring-boot

I have two entities, one is Client and the other is Transactions and they are linked #OneToMany respectively.
I would like to create a query using Querydsl that will filter my clients based on the count of their transactions. Basically I want this SQL query
SELECT * FROM client WHERE id IN (SELECT client_id
FROM (SELECT client_id, count(client_id) as 'count' from transaction group by client_id) as t
where t.count >= 10)
to be done in Querydsl. The problem is I want to return a BooleanExpression so I can aggregate the query further, i.e AND it or OR it with some other query.
The Querydsl entities are QClient and QTransaction respectively.
This is my current code,
QClient client = QClient.client1;
QTransaction transaction = QTransaction.transaction;
var count = Expressions.numberPath(Long.class, "count");
var subquery = JPAExpressions.select(transaction.client.id, transaction.client.id.count().as(count))
.from(transaction).groupBy(transaction.client.id).where(count.gt(depositCount)).select(transaction.client.id);
return client.id.in(subquery);
It doesn't work in this form.

How about this?
Note this is in context to my test project at https://github.com/gtiwari333/spring-boot-web-application-seed. But you can easily apply the same for your tables.
Ref: https://github.com/gtiwari333/spring-boot-web-application-seed/blob/master/main-app/src/main/java/gt/app/modules/article/ArticleRepositoryCustomImpl.java#L48
public void doQuery() {
QArticle qArticle = QArticle.article;
QUser user = QUser.user;
var subquery1 = JPAExpressions
.select(qArticle.createdByUser.id)
.from(qArticle)
.groupBy(qArticle.createdByUser.id)
.having(qArticle.id.count().gt(5));
BooleanExpression exp = user.id.in(subquery1);
BooleanExpression exp2 = qArticle.title.length().lt(15);
List<Article> ar = from(qArticle)
.join(user).on(qArticle.createdByUser.id.eq(user.id))
.select(qArticle)
.where(exp.and(exp2))
.fetch();
System.out.println(ar);
}
Also, your query can be simplified.
SELECT * FROM client WHERE id IN
( SELECT client_id FROM
(SELECT client_id, count(client_id) as 'count' from ransaction group by client_id)
as t where t.count >= 10)
to:
SELECT * FROM client
WHERE id IN ( SELECT client_id
FROM
TRANSACTION
GROUP BY
client_id
HAVING
count(client_id)> 10)

Related

LINQ select row with max value after group by

I have following sql statement:
Select
tsl.Transaction_Id,
tsl.State_Id,
MAX(tsl."Timestamp")
from TransactionStatesLog tsl
group by tsl.Transaction_Id
How this statement can be translated to LINQ? I just want to select the whole row, where Timestamp is maximum of the group.
With this code i am able just to select TransactionId and max Timestamp from the group.
var states = (from logs in _context.TransactionStatesLog
group logs by new { logs.TransactionId } into g
select new
{
TransactionId = g.Key,
Timestamp = g.Max(x => x.Timestamp)
}).ToList();
I am working with ef core 3.1
Assuming you forgot to add tsl.State_Id to your SQL as grouping key as follows(otherwise that SQL does not work either):
Select
tsl.Transaction_Id,
tsl.State_Id,
MAX(tsl."Timestamp")
from TransactionStatesLog tsl
group by tsl.Transaction_Id, tsl.State_Id
If I understood you correctly you need to add StateId to grouping statement as well so that you will be able to select StateId and TransactionId.
So this should work:
var states = (from logs in _context.TransactionStatesLog
group logs by new { logs.TransactionId, logs.StateId } into g
select new
{
TransactionId = g.Key.TransactionId,
StateId = g.Key.StateId,
Timestamp = g.Max(x => x.Timestamp)
}).ToList();
See: Group by with multiple columns using lambda

Select from junction table

everybody!
What should I do if I need to make select from junction table?
For example, I develop project and I need to make chats between users. I have two entities: User and Chat, and many-to-many relation between them (accordingly, I have three tables: user, chat, chat_user). I try to get all chats, which user is member, and to get all users from these chats.
I made the following SQL query:
SELECT *
FROM chat c
INNER JOIN chat_user cu ON c.id = cu.chat_id
INNER JOIN user u ON u.id = cu.user_id
WHERE c.id IN (SELECT chat_id
FROM chat_user
WHERE user_id = <idUser>);
But I don't know how to translate in DQL subquery SELECT chat_id FROM chat_user WHERE user_id = <idUser>, because a haven't additional entity for table chat_user.
And I tried to add entity ChatUser and get data in ChatRepository smt. like this:
public function getChatsData($idUser)
{
$subQuery = $this->getEntityManager()
->getRepository(ChatUser::class)
->createQueryBuilder('chus')
->select('chus.chat')
->andWhere('chus.user = :idUser')
->setParameter('idUser', $idUser)
;
$qb = $this->createQueryBuilder('c');
return $qb
->innerJoin('c.chatUsers', 'cu')
->addSelect('cu')
->innerJoin('cu.user', 'u')
->addSelect('u')
->innerJoin('c.messages', 'm')
->addSelect('m')
->andWhere('u.id = :idUser')
->andWhere($qb->expr()->in(
'c.id',
$subQuery->getDQL()
))
->setParameter('idUser', $idUser)
->getQuery()
->getResult()
;
}
but it doesn't work. I get error [Semantical Error] line 0, col 12 near 'chat FROM App\Entity\ChatUser': Error: Invalid PathExpression. Must be a StateFieldPathExpression.
Have Doctrine standard tools for such tasks?

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.

How to select different fields using .Concat for Union All query

I’m doing some preliminary work getting data consumption ready from a webAPI controller in MVC. I’m using a linq query that mimics a union query that uses two joins. However, I need the select statement to ask for a different field on each side of the union. This works fine in SSMS when I run the query, but when I create the query in linq it will not allow the fields to be different. It gives an error saying that each anonymous type requires a receiver of the same type. This is referring to the two anonymous types created via select new { }… Here’s the two queries.
In SQL:
Select m.Last_Name, first_name, m.dc_number, Dept_Job as JobOrStatus FROM
Master_Roster m
INNER JOIN Class_Assignment a on m.dc_number = a.dc_number
where a.subject_am = 'y1'
AND Start_Date_AM <= '1/31/18'
UNION ALL
Select m.Last_Name, first_name, m.dc_number, status_am FROM Master_Roster m
INNER JOIN Attend_am_y1 at on m.dc_number = at.dc_number
where at.class_date_am >= '1/1/18'
AND at.class_date_am <= '1/31/18'
ORDER BY Last_Name
In linq:
(from m in db.Master_Roster
join c in db.Class_Assignment
on m.dc_number equals c.dc_number
orderby m.Last_Name
where c.Subject_AM == "y1"
select new { m.dc_number, m.Last_Name, m.First_Name }).Concat(
from m in db.Master_Roster
join a in db.attend_am_y1 on m.dc_number equals a.dc_number
orderby m.Last_Name
where a.class_date_am >= date1 &&
a.class_date_am <= date2
select new { m.dc_number, m.Last_Name, m.First_Name });
If I were to add dept_job and status_am to the linq query, it throws aforementioned error. I’d like to do this without using a stored procedure. Any ideas?
Instead of putting into 2 anonymous types, put into the same viewmodel and concat. Eg:
Internal Class ConcatViewModel{
public int DcNumber {get;set;}
public string LastName {get;set;)
// etc...
}
Then:
select new ConcatViewModel{
DcNumber = m.dc_number,
LastName = m.Last_Name,
FirstName = m.First_Name,
Status = m.Dept_Job
}
and
select new ConcatViewModel{
DcNumber = m.dc_number,
LastName = m.Last_Name,
FirstName = m.First_Name,
Status = m.status_am
}
(assuming that status_am and Dept_Job are the same underlying type)

How to write Order by expression in JPQL

PostgreSQL and MySQL offers to write expression into ORDER BY clause in SQL query. It allows to sort items by some column but the special values are on the top. The SQL looks like this one. ( works in Postgres )
select * from article order by id = 4, id desc;
Now I want to write it in the JPQL but it doesn't work. My attempt is:
#NamedQuery(name = "Article.special", query = "SELECT a FROM Article a ORDER BY ( a.id = :id ) DESC, a.id DESC")
This is JPA 1.0 with Hibernate driver. Application server throws this exception on deploy.
ERROR [SessionFactoryImpl] Error in named query: Article.special
org.hibernate.hql.ast.QuerySyntaxException: unexpected AST node: = near line 1, column 73 [SELECT a FROM cz.cvut.fel.sk.model.department.Article a ORDER BY ( a.id = :id ) DESC, a.id DESC]
at org.hibernate.hql.ast.QuerySyntaxException.convert(QuerySyntaxException.java:54)
Thanks a lot.
For a named query, (ORDER BY ( a.id = :id ) or ORDER BY (:id )) won't work as DSC/ASC can't be parametrized at run-time.
1) Dynamic way if ordering element varies at runtime.
String query = "SELECT a FROM Article a ORDER BY "+orderElement+" DESC, a.id DESC";
entityManager.createQuery(query).getResultList();
2) Static way in entity bean if ordering element is fixed.
Field level:
#OrderBy("id ASC")
List<Article> articles;
Method level:
#OrderBy("id DESC")
public List<Article> getArticles() {...};

Resources