Sum a List of averages in spring boot jpa query - spring-boot

I have this query thats return a list of averages
#Query("SELECT AVG(p.quantity) FROM Position p GROUP BY p.client.id") public List<Float> findAVGPositions();
But I want to return SUM of averages like this, but it doesn't work
#Query("SELECT SUM(averages) FROM (SELECT AVG(p.quantity) as averages FROM Position p GROUP BY p.client.id)") public Float findSumAVGPositions();
Can you help me? thanks

Unfortunately, in JPQL, nested selects are not permitted in FROM clause. They are allowed only in SELECT and WHERE.
You have two approaches available.
(1) Use findAVGPositions and calculate sum in Java
findAVGPositions().stream().reduce(0f, Float::sum);
(2) Use a native SQL query
#Query(value = "SELECT SUM(averages) " +
"FROM (SELECT AVG(p.quantity) AS averages " +
"FROM position p " +
"GROUP BY client_id) AS averages_select",
nativeQuery = true)
public Float findSumAVGPositions();
Depending on the database you use, AS averages_select alias might be needed or not (PostgreSQL requires it, even if it's not used).
Please, pay attention to use the correct names of a database table for Position entity and a database column for a foreign key client.id.
In my example, I assumed a standard mapping convention: position as a table name, and client_id as a foreign key column.

Related

List of multiple column condition in query (kind of batch)

when trying to search with single record then this query works
#Query(value = "select * from table t where t.column1 = :column1 and t.column2 = :column2 and t.column3 = :column3")
Flux<Invoice> findByMultipleColumn(#Param("column1”) String column1, #Param("column2”) String column2, #Param("column3”) String column3);
But when I have list of criterias instead of a single row condition then I have to loop over the list of criterias & call the above query multiple times which is not feasible solution.
Sudo code
for (Criteria criteria : criteriaList) {
repository.findByMultipleColumn(criteria.getColumn1(), criteria.getColumn2(), criteria.getColumn3());
}
What I am trying to find a way to solve the above query for multiple LIST of all the 3 column criteria pair, something like below (this is not working solution)
#Query(value = "select * from table t where t.column1 = :column1 and t.column2 = :column2 and t.column3 = :column3")
Flux<Invoice> findByMultipleColumn(#Param List<Table> table);
Is there any way somehow we can try to achieve the above case?
Would be doable if column1, 2 and 3 were Embedded, then you could do
#Query(select * from Entity where embeddedProperty in (:values))
Flux<Entity> findByEmbeddedPropertyIn(Collection<EmbeddedClas> values);
Which would generate the following native SQL clause
Where (column1, column2, column3) in ((x, y, z), ...)
If you don't want to pack these fields i to an embeddable class, you can also try to do a workaround
#Query(select * from Entity where Concat(column1, ';', column2, ';', column3) in (:parametersConcatrenatedInJava)
Flux<Entity> findBy3Columns(Collection<String> parametersConcatrenatedInJava);
It's ofcourse not bulletproof, all three columns could have ";" as their values, this might be problematic if their type is not string, etc.
Edit.:
Third option is to use specification api. Using the criteria builder you can concatenate multiple and / or queries. And pass that specification as an argument to the repository that extends JpaSpecificationExecutor (if you're fetching whole entities) or an entity manager if you're using projections. Read more about specifications

Query ManyToMany SpringBoot

I have the following modeling:
table hackathon
idEvent int pk ai
...
table team
idTeam int pk ai
...
table hackathon_has_team
hackathon_id int
team_id int
....
And I need to select all the teams that are in an event by event id and I'm breaking my head with it, can anyone help?
I'm trying to do this but it does not work:
#Query("SELECT t "
+ "FROM Team t INNER JOIN hackathon_has_team "
+ "ON hackathon_has_team.team_id = t.id "
+ "WHERE hackathon_has_team.hackathon_id = :hackathon_id")
public Page<Team> getListAllTeamsByIdOfHackathon(#Param("hackathon_id ") Long id, Pageable pageable);
You might need to provide more to get more, but depending on how you are obtaining the list of the teams, you could try:
select * from event_has_team
where (select team_id from table team)
This selects everything from the event_has_team table where the team_id is in the team table. you can also add a where clause for teams you've identified in that sub-select or nit he main select, depending on what you really need.

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.

Fetch dates (by month or year) stored in TIMESTAMP using JPA

I am facing a problem and I would like you to help me.
It turns out I have one table in my Oracle 11g database where I store failures of one electronic device. The table definition is following:
CREATE TABLE failure
( failure_id NUMERIC NOT NULL
, fecha TIMESTAMP NOT NULL
, module_id NUMERIC NOT NULL
, code NUMERIC
, PRIMARY KEY(failure_id)
);
Where 'fecha' means 'date'.
I need to fetch failures by YEAR or by MONTH for one specific module but I can't. My ORM maps the TIMESTAMP type to java.sql.Date but I don't know how to compare the month in the JPQL sentence. I have tried to use ORACLE functions with native queries but I front with another issue: to cast the results.
I am using JPA 2.0 with Eclipselink 2.3.2.
My doubts are:
Can I use Oracle functions with this version of Eclipselink library? My experience say no.
Query query = entityManager.createQuery("SELECT f FROM Failure f "
+ "WHERE EXTRACT(YEAR FROM f.fecha) = ?1 "
+ "AND f.moduleId.moduleId = ?2");
query.setParameter(1, year);
query.setParameter(2, idModule);
I get this error: Unexpected token [(]
Can I use Eclipselink functions? My experience say no.
Query query = entityManager.createQuery("SELECT f FROM Failure f "
+ "WHERE EXTRACT('YEAR', f.fecha) = ?1 "
+ "AND f.moduleId.moduleId = ?2");
query.setParameter(1, year);
query.setParameter(2, idModule);
Same error.
Do you know a simple way to fetch this data using only one query?
I know I can fetch one module and then check failures with loops but I think it is not the best performing solution.
Thanks.
My sources:
Eclipselink JPA functions link
Eclipselink Query Enhancements link
A native query is written in the SQL dialect of your DB so can use DB specific functionality see the createNativeQuery methods of the EntityManager.
However there is another solution, test the timestamp against a lower and upper value:
WHERE f.fecha >= '2012-9-1' AND f.fecha < '2012-10-1'
The syntax in EclipseLink 2.4 for EXTRACT is,
EXTRACT(YEAR, f.fecha)
http://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Basic_JPA_Development/Querying/JPQL#Functions
I used Eclipselink v 2.4 functions and I am getting values using this:
Query query = entityManager.createQuery("SELECT f FROM Failure f "
+ "WHERE SQL('EXTRACT (YEAR FROM ?)', f.fecha) = ?1 "
+ "AND f.moduleId.moduleId = ?2 ");
Extracting year from date stored in database avoids to me one comparison between two dates.
Use
Query query = entityManager.createQuery("SELECT f FROM Failure f "
+ "WHERE EXTRACT(YEAR from f.fecha) = ?1 "
+ "AND f.moduleId.moduleId = ?2");
I was facing the same problem. I only checked for the correct syntax of that EXTRACT function for oracle and it worked for me! (Notice the FROM clause into the function syntax.
#NamedQuery(name = "Registros.findByFechacaptura", query = "SELECT s FROM Registros s WHERE EXTRACT(YEAR FROM s.fechacaptura) = EXTRACT(YEAR FROM :fechacaptura)")

Doctrine : subquery in DQL

I'm trying to calculate a value using DQL on one single table. Say this table is named "TABLE". It has 5 colums :
id
people_count
animal_count
region_id
type_id
The result I'm looking for is the sum of people (when grouped by region), divided by the sum of animals (when grouped by type);
The SQL would be something like that :
SELECT SUM(people_count) /
(
SELECT SUM(animal_count)
FROM TABLE t2
GROUPED BY type_id
)
FROM TABLE t1
GROUPED BY region_id
How would you do that in Doctrine using DQL?
I resolved my problem by creating another query, executing it and including the result in the first one.
This is probably not the best way to do if you are dealing with a simple example, but it was the only solution for me regarding to my code architecture.
I have a solution but I think there is probably a best solution to resolve your problem.
In any case, make two queries and import results of first query in the second can be a solution. Unfortunately, it's a low-usage for our database. More, sometimes we must execute only one SQL to sort on column result per example.
<?php
namespace AppBundle\Repository;
use AppBundle\Entity\MyClass;
use Doctrine\ORM\EntityRepository;
class MyRepository extends EntityRepository
{
public function findWithSubQuery()
{
return $this->createQueryBuilder('a')
->addSelect(sprintf(
"(SELECT COUNT(b.id) FROM %s AS b WHERE b.a = a.id GROUP BY a.id) AS otherColumn",
MyClass::class
))
;
}
}
I use this solution. Maybe the subquery could be write with DQL ojbect rather that DQL string.

Resources