I'm using queryDSL to get users with some additional data from base:
public List<Tuple> getUsersWithData (final SomeParam someParam) {
QUser user = QUser.user;
QRecord record = QRecord.record;
JPQLQuery = query = new JPAQuery(getEntityManager());
NumberPath<Long> cAlias = Expressions.numberPath(Long.class, "cAlias");
return query.from(user)
.leftJoin(record).on(record.someParam.eq(someParam))
.where(user.active.eq(true))
.groupBy(user)
.orderBy(cAlias.asc())
.list(user, record.countDistinct().as(cAlias));
}
Despite it's working as desired, it generates two COUNT() in SQL:
SELECT
t0.ID
t0.NAME
to.ACTIVE
COUNT(DISTINCT (t1.ID))
FROM USERS t0 LEFT OUTER JOIN t1 ON (t1.SOME_PARAM_ID = ?)
WHERE t0.ACTIVE = true
GROUP BY t0.ID, to.NAME, t0.ACTIVE
ORDER BY COUNT(DISTINCT (t1.ID))
I want to know if it's possible to get something like this:
SELECT
t0.ID
t0.NAME
to.ACTIVE
COUNT(DISTINCT (t1.ID)) as cAlias
FROM USERS t0 LEFT OUTER JOIN t1 ON (t1.SOME_PARAM_ID = ?)
WHERE t0.ACTIVE = true
GROUP BY t0.ID, to.NAME, t0.ACTIVE
ORDER BY cAlias
I failed to understand this from documentation, please, give me some directions if it's possible.
QVehicle qVehicle = QVehicle.vehicle;
NumberPath<Long> aliasQuantity = Expressions.numberPath(Long.class, "quantity");
final List<QuantityByTypeVO> quantityByTypeVO = new JPAQueryFactory(getEntityManager())
.select(Projections.constructor(QuantityByTypeVO.class, qVehicle.tipo, qVehicle.count().as(aliasQuantity)))
.from(qVehicle)
.groupBy(qVehicle.type)
.orderBy(aliasQuantity.desc())
.fetch();
select
vehicleges0_.type as col_0_0_, count(vehicleges0_.pk) as col_1_0_
from vehicle vehicleges0_
group by vehicleges0_.type
order by col_1_0_ desc;
I did something like that, but I did count first before ordering. Look the query and the select generated.
That's a restriction imposed by SQL rather than by queryDSL.
You may try to run your suggested query in a DB console - I think it won't execute, at least not on every DB.
But I don't think this duplicated COUNT() really creates any performance overhead.
Related
tl;dr I have a long native query in Hibernate, that is split in multiple queries (with table as ...). This Query use inner joins that not work in hibernate (empty result), but work excellent in the SQL Console. Also if I copy/paste the query from the hibernate logs into the console.
with params as (select ca.id as calculation_id, chart_id, range_size, range_size * interval '1' minute as size
from calculation ca
inner join chart c on c.id = ca.chart_id
inner join time_range tr on tr.id = c.time_range_id
where ca.id = :calculationid),
tuple_diff as (select t.calculation_id,
t.id,
t.ohlc_id,
t.time, t.time - lag(t.time, 1) over (order by t.time) as diff
from tuple t inner join params p
on t.calculation_id = p.calculation_id),
ohlc_diff as (select o.chart_id,
o.id,
o.time,
o.time - lag(o.time, 1) over (order by o.time) as diff
from ohlc o inner join params p on o.chart_id = p.chart_id),
tuple_filtered as (select id, ohlc_id, time, diff, p.size, p.range_size
from tuple_diff t
inner join params p on chart_id = p.chart_id
where diff <> p.size),
ohlc_filtered as (select o.chart_id, o.id as ohlc_id, time
from ohlc_diff o
inner join params p on o.chart_id = p.chart_id
where o.diff <> p.size)
select t.time as time, t.range_size as size
from tuple_filtered t
left join ohlc_filtered o on t.time = o.time
where o.ohlc_id is null
order by t.time;
The long version, I have a microservice, generated with jhipster and the project runs productive with PostgreSQL and in development mode with a H2 Database. I have ohlc stockexchange time series and calculate calculations with an other microservice and store the computation in a tuple table. Sometimes I have holes in the timeline and want to recalculate them. I need to get the time difference to the last calculation. This is not an easy SQL Query, because you need to access the previous row like in Excel. I use the lag function to solve this problem. But the lag function is not the reason.
I generate a test project with an integration test to analyze the problem. The Junit Test is de.bitc.se.service.CalculationServiceIT and run with a Postgresql Testcontainer. The test also fill the SQL tables with testdata. I start to split the query in small parts.
with params as (select ca.id as ca_id,
chart_id, range_size,
range_size * interval '1' minute as size
from calculation ca
inner join chart c on c.id = ca.chart_id
inner join time_range tr on tr.id = c.time_range_id
where ca.id = :calculationid)
select ca_id, chart_id, range_size
from params
order by ca_id;
This part works like the console.
with tuple_diff as (select t.calculation_id,
t.id,
t.ohlc_id,
t.time,
t.time - lag(t.time, 1) over (order by t.time) as diff
from tuple t
where calculation_id = :calculationid)
select calculation_id, time
from tuple_diff
order by calculation_id;
The second query too, but when I try to join the query's, I got an empty result:
with params as (select ca.id as ca_id,
chart_id,
range_size, range_size * interval '1' minute as size
from calculation ca
inner join chart c on c.id = ca.chart_id
inner join time_range tr on tr.id = c.time_range_id
where ca.id = :calculationid),
tuple_diff as (select t.calculation_id,
t.id,
t.ohlc_id,
t.time,
t.time - lag(t.time, 1) over (order by t.time) as diff
from tuple t
inner join params p on p.ca_id = t.calculation_id
where calculation_id = ca_id)
select calculation_id
from tuple_diff
order by calculation_id;
When I try to join the param query (one result) with the tuple table I got no result. In the SQL console I got a result of multiple lines. I used the EntityManager to exclude Spring Data code in the splitted queries in the integration test.
I have no idea why I got no result. Is it a bug in Hibernate or a mistake?
I am constructing and running a query via this Hibernate-based code:
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Tuple> criteria = cb.createTupleQuery();
Root<hisaVO> hisa = criteria.from(hisaVO.class);
Root<EstablecVO> establec = criteria.from(EstablecVO.class);
Root<DisaVO> disa = criteria.from(DisaVO.class);
Root<RedVO> red1 = criteria.from(RedVO.class);
Root<MicroredVO> microred = criteria.from(MicroredVO.class);
Root<Unidad_EjecutoraVO> ue1 = criteria.from(Unidad_EjecutoraVO.class);
Join<hisaVO,EstablecVO> j1 = hisa.join("estab");
Join<EstablecVO,DisaVO> j2 = j1.join("disa") ;
Join<EstablecVO,RedVO> j3 = j1.join("red") ;
Join<EstablecVO,MicroredVO> j4 = j1.join("microred") ;
Join<EstablecVO,Unidad_EjecutoraVO> j5 = j1.join("ue") ;
criteria.multiselect(j3.get("red_nombre"), cb.count(hisa))
.groupBy(red1.get("red_nombre"));
return em.createQuery(criteria).getResultList();
The log shows Hibernate is implementing that via this corresponding SQL:
select
redvo3_.red_nombre as col_0_0_,
count(hisavo0_.id) as col_1_0_
from
hisa hisavo0_
inner join establec establecvo6_
on hisavo0_.cod_estab=establecvo6_.COD_ESTAB
inner join disa disavo7_
on establecvo6_.cod_disa=disavo7_.id
inner join red redvo8_
on establecvo6_.cod_red=redvo8_.id
inner join microred microredvo9_
on establecvo6_.cod_mic=microredvo9_.id
inner join unidad_ejecutora unidad_eje10_
on establecvo6_.cod_ue=unidad_eje10_.id
cross join establec establecvo1_
cross join disa disavo2_
cross join red redvo3_
cross join microred microredvo4_
cross join unidad_ejecutora unidad_eje5_
group by redvo3_.red_nombre
It seems to be adding extra, unexpected cross joins at the end of the query. Why is it doing that?
You give your query multiple roots via multiple invocations of CriteriaQuery.from(). Each one after the first is reflected in the final query via a cross join. That's roughly what it means to be a query root.
You do not need to (and should not) use CriteriaQuery.from() to add entities to the query that you mean to be associated via inner joins corresponding to mapped relationships -- those you connect via a Join used as the Selection when you run your query.
Can anybody help me to create JPA Criteria Builder query in order to achieve this ?:
select id from (
select distinct r.id
r.date
r.name
from report r
inner join unit u
on u.report_id = r.id
order by
r.date desc,
r.name asc)
where rownum <= 10
I just can create inner query:
CriteriaQuery<Object[]> innerQuery = cb.createQuery(Object[].class);
Root<ReportEntity> root = innerQuery.from(ReportEntity.class);
List<Ojbect[]> resultLsit = em.createQuery(
innerQuery.multiselect(root.get(ReportEntity_.id),
root.get(ReportEntity_.date),
root.get(ReportEntity_.name)
.distinct(true)
.orderBy(cb.desc(root.get(ReportEntity_.date)),
cb.asc(root.get(ReportEntity.name))
).setMaxResults(10).getResultList();
Thx in advance :)
I've decided to get List of Object[] and then retrieve id from array
List idList = resultList.stream().map(array -> (Long)array[0]).collect(Collectors.toList());
This is code smell, but unfortunatelly I haven't found better solution.
Note I use this approach to cope Hibernate issue :
"Warning “firstResult/maxResults specified with collection fetch; applying in memory!”? - this warning pops up due to using fetch and setMaxResults in hql or criteria query.
That's why first of all I get all id, and then I find all entities according this id. (select * from ReportEntity r where r.id in :idList) - smth like this.
I am quite new to JPA and I have encountered a problem with understanding one particular query. I have rewritten in to the CriteriaQuery but the result and the query translated to SQL is not correct.
Background situation: I have a table of store transaction (moves) and the current amount in the store is defined as a sum of all changes. Now I want to select and display the last moves as they also contain an information about the resulting amount on store.
So, this is the query in JPQL:
SELECT m FROM move m WHERE m.id = (
SELECT MAX(o.id) FROM move o WHERE (o.item = m.item AND m.cell.store = :s)
I tried to rewrite it to the following CriteriaQuery:
CriteriaBuilder builder = model.getCriteriaBuilder();
CriteriaQuery<Move> query = builder.createQuery(Move.class);
Root<Move> root = query.from(Move.class);
Subquery<Long> subquery = query.subquery(Long.class);
Root<Move> subroot = subquery.from(Move.class);
subquery.select(builder.max(subroot.get("id").as(Long.class)));
subquery.where(builder.and(
builder.equal(
subroot.get("item").get("id"),
root.get("item").get("id")),
builder.equal(
subroot.get("cell").get("store").as(Store.class),
store)));
Expression<Boolean> where = builder.equal(
root.get("id"),
subquery);
query.where(where);
return model.getList(query);
The incorrect SQL query produced is following:
SELECT t0.id, (...) FROM move t0 WHERE (t0.id = (
SELECT MAX(t1.id) FROM item t3, item t2, move t1
WHERE (((t2.id = t3.id) AND
(t1.cell_store = ?)) AND
((t2.id = t1.item_ref) AND
(t3.id = t0.item_ref))))
)
I do not understand why there is a double cross join in the subquery. Thank you for helping!
In JPQL I want to construct the equivalent query to this:
select *, count(*) as finger_count from page_delta_summary
where delta_history_id = ? and change_type = ? group by fingerprint;
where fingerprint is a varchar field in table page_delta_summary. What I have is this:
select d, count(d) as finger_count from PageDeltaSummary d
where d.deltaHistoryId = :deltaHistoryId and d.type = :pageDeltaType
GROUP BY d.fingerprint"
where PageDeltaSummary is my entity. But I'm getting the following exception:
org.apache.openjpa.persistence.ArgumentException: Your query on type "class com.su3analytics.sitedelta.model.PageDeltaSummary" with filter "select d, count(d) from PageDeltaSummary d where d.deltaHistoryId = :deltaHistoryId and d.type = :pageDeltaType GROUP BY d.fingerprint" is invalid. Your select and having clauses must only include aggregates or values that also appear in your grouping clause.
The query works fine if I remove either count(d) as finger_count or the GROUP BY.
Any suggestions?
Thanks
Your original SQL query doesn't make sense, therefore you can't convert in into JPQL.
I guess you want to get count of page_delta_summary rows satisfying where conditions for each fingerprint. If so, the SQL query looks like this:
select fingerprint, count(*) as finger_count from page_delta_summary
where delta_history_id = ? and change_type = ? group by fingerprint;
and JPQL - like this:
select d.fingerprint, count(d) from PageDeltaSummary d
where d.deltaHistoryId = :deltaHistoryId and d.type = :pageDeltaType
GROUP BY d.fingerprint
These queries return pairs <fingerprint, finger_count> instead of full page_delta_summary rows (or entities).