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.
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'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.
While running Hibernate Criteria, HQL query is not created in given order
and shows Exception
ERROR SqlExceptionHelper:131 - Unknown column 'stsup4_.SUPPLIER_ID' in 'on clause'
The Generated HQL is not created in expected order.
When I run the same generated HQL in mysql with some changes, I get output.
Java Code is given Below
Session session = connector.getSession();
Criteria cr = session.createCriteria(ST_PRODUCTMASTER.class, "ST_PRODUCT");
cr.createCriteria("ST_PRODUCT.ST_PRODUCT_MANUFACTURER_PARENT", "STPRODMAN", JoinType.LEFT_OUTER_JOIN, Restrictions.eq("PROD_MAN_STATUS", "ACTIVE"));
cr.createCriteria("STPRODMAN.ST_MANUFACTURER", "STMAN", JoinType.LEFT_OUTER_JOIN);
cr.createCriteria("ST_PRODUCT.ST_PRODUCT_SUPPLIER_PARENT", "STPRODSUP", JoinType.LEFT_OUTER_JOIN, Restrictions.eq("PROD_SUPPLIER_STATUS", "ACTIVE"));
cr.createCriteria("STPRODSUP.ST_SUPPLIER", "STSUP", JoinType.LEFT_OUTER_JOIN);
cr.createCriteria("ST_PRODUCT.ST_PRODUCT_RATES", "ZSTPR", JoinType.LEFT_OUTER_JOIN, Restrictions.eqProperty("ST_SUPPLIER.SUPPLIER_ID", "STSUP.SUPPLIER_ID"));
cr.createCriteria("ST_PRODUCT.ST_MEDICINE_CATEGORY", "MEDI_CAT", JoinType.LEFT_OUTER_JOIN);
cr.createCriteria("ST_PRODUCT.ST_TAX_MASTER", "TAX", JoinType.INNER_JOIN);
cr.createCriteria("ST_PRODUCT.ST_UNIT", "UNIT", JoinType.INNER_JOIN);
cr.add(Restrictions.eq("ST_PRODUCT.PRODUCT_STATUS", "ACTIVE"));
ProjectionList p1 = Projections.projectionList();
p1.add(Projections.property("ST_PRODUCT.PRODUCT_ID"));
p1.add(Projections.property("ST_PRODUCT.PRODUCT_NAME"));
p1.add(Projections.property("UNIT.UNIT_DISPLAY_UNIT"));
p1.add(Projections.property("TAX.TAX_PURCHASE"));
p1.add(Projections.property("ST_PRODUCT.PRODUCT2_DISCOUNT"));
p1.add(Projections.property("ST_PRODUCT.PRODUCT2_DIS_AMOUNT"));
p1.add(Projections.property("ST_MEDICINE_CATEGORY.CAT_CATEGORY_ID"));
p1.add(Projections.property("MEDI_CAT.CAT_SHORT_NAME"));
p1.add(Projections.property("UNIT.UNIT_ID"));
p1.add(Projections.property("TAX.TAX_ID"));
//MANUFACTURER
p1.add(Projections.property("STMAN.MAN_ID"));
p1.add(Projections.property("STPRODMAN.BASE_LEVEL"));
p1.add(Projections.property("STPRODMAN.FREE_QTY"));
//SUPPLIER
p1.add(Projections.property("STSUP.SUPPLIER_ID"));
p1.add(Projections.property("STPRODSUP.BASE_LEVEL"));
p1.add(Projections.property("STPRODSUP.FREE_QTY"));
cr.setProjection(p1);
List l = cr.list();
System.out.println("Size items::" + l.size());
ERROR SqlExceptionHelper:131 - Unknown column 'stsup4_.SUPPLIER_ID' in 'on clause'
This it the SQL generated by Hibernate
select
this_.PRODUCT_ID as y0_,
this_.PRODUCT_NAME as y1_,
unit8_.UNIT_DISPLAY_UNIT as y2_,
tax7_.TAX_PURCHASE as y3_,
this_.PRODUCT2_DISCOUNT as y4_,
this_.PRODUCT2_DIS_AMOUNT as y5_,
this_.CAT_CATEGORY_ID as y6_,
medi_cat6_.CAT_SHORT_NAME as y7_,
unit8_.UNIT_ID as y8_,
tax7_.TAX_ID as y9_,
stman2_.MAN_ID as y10_,
stprodman1_.BASE_LEVEL as y11_,
stprodman1_.FREE_QTY as y12_,
stsup4_.SUPPLIER_ID as y13_,
stprodsup3_.BASE_LEVEL as y14_,
stprodsup3_.FREE_QTY as y15_
from
ST_PRODUCTMASTER this_
left outer join
ST_MEDICINE_CATEGORY medi_cat6_
on this_.CAT_CATEGORY_ID=medi_cat6_.CAT_CATEGORY_ID
left outer join
ST_PRODUCT_MANUFACTURER stprodman1_
on this_.PRODUCT_ID=stprodman1_.PRODUCT_ID
and (
stprodman1_.PROD_MAN_STATUS=?
)
left outer join
ST_MANUFACTURER stman2_
on stprodman1_.MAN_ID=stman2_.MAN_ID
left outer join
ST_PRODUCT_RATES zstpr5_
on this_.PRODUCT_ID=zstpr5_.PRODUCT_ID
and (
zstpr5_.SUPPLIER_ID=stsup4_.SUPPLIER_ID
)
left outer join
ST_PRODUCT_SUPPLIER stprodsup3_
on this_.PRODUCT_ID=stprodsup3_.PRODUCT_ID
and (
stprodsup3_.PROD_SUPPLIER_STATUS=?
)
left outer join
ST_SUPPLIER stsup4_
on stprodsup3_.SUPPLIER_ID=stsup4_.SUPPLIER_ID
inner join
ST_TAX_MASTER tax7_
on this_.TAX_ID=tax7_.TAX_ID
inner join
ST_UNIT unit8_
on this_.UNIT_ID=unit8_.UNIT_ID
where
(
this_.PRODUCT_DELETED <> 'DELETED'
)
and this_.PRODUCT_STATUS=?
zstpr5_ is generated before stsup4_
and thus zstpr5_.SUPPLIER_ID=stsup4_.SUPPLIER_ID
is not accessible
left outer join
ST_PRODUCT_RATES zstpr5_
on this_.PRODUCT_ID=zstpr5_.PRODUCT_ID
and (
zstpr5_.SUPPLIER_ID=stsup4_.SUPPLIER_ID
)
left outer join
ST_PRODUCT_SUPPLIER stprodsup3_
on this_.PRODUCT_ID=stprodsup3_.PRODUCT_ID
and (
stprodsup3_.PROD_SUPPLIER_STATUS=?
)
left outer join
ST_SUPPLIER stsup4_
on stprodsup3_.SUPPLIER_ID=stsup4_.SUPPLIER_ID
ACTUAL EXPECTED ORDER IS
left outer join
ST_PRODUCT_SUPPLIER stprodsup3_
on this_.PRODUCT_ID=stprodsup3_.PRODUCT_ID
and (
stprodsup3_.PROD_SUPPLIER_STATUS=?
)
left outer join
ST_SUPPLIER stsup4_
on stprodsup3_.SUPPLIER_ID=stsup4_.SUPPLIER_ID
left outer join
ST_PRODUCT_RATES zstpr5_
on this_.PRODUCT_ID=zstpr5_.PRODUCT_ID
and (
zstpr5_.SUPPLIER_ID=stsup4_.SUPPLIER_ID
)
Hibernate is generating a number with table name,
1_, 2_ , 3_
even though while creating SQL this order is not maintained,
it is not even created in the order we give.
Since you haven't posted your mapping file, I'll explain how Hibernate orders the joins.
Hibernate uses the mapping xml file to figure out the order of joins, so if you have a relation in it (one-to-many or any other tag) for that table, just move it above the other table's tag.
You can read more about it here.
I create dao layer using spring data as follow
#Query(value="SELECT p FROM Products p INNER JOIN FETCH p.categoriesProducts cp INNER JOIN FETCH cp.categoryId c INNER JOIN FETCH p.mediasList ml ")
public List<Products> getAllProduct(Pageable pageable);
and I call it as follow
PageRequest pr = new PageRequest(0,100) ;
List<Products> ls = this.repo.getProductRepo().getAllProduct(pr) ;
my issue is I dont see the limit key word in the printed SQL in the log which is shown as follow
select products0_.id as id1_8_0_, categories1_.product_id as product_1_2_1_, categories2_.id as id1_1_2_, mediaslist3_.id as id1_4_3_, products0_.description as descript2_8_0_, products0_.discount_price as discount3_8_0_, products0_.price as price4_8_0_, products0_.prod_condition as prod_con5_8_0_, products0_.prod_number as prod_num6_8_0_, products0_.qty as qty7_8_0_, products0_.title as title8_8_0_, categories1_.category_id as category2_2_1_, categories2_.cat_name as cat_name2_1_2_, categories2_.description as descript3_1_2_, categories2_.keywords as keywords4_1_2_, categories2_.parent_id as parent_i5_1_2_, mediaslist3_.big_image_file as big_imag2_4_3_, mediaslist3_.media_type as media_ty3_4_3_, mediaslist3_.product_id as product_6_4_3_, mediaslist3_.thumb_image_file as thumb_im4_4_3_, mediaslist3_.video_file as video_fi5_4_3_, mediaslist3_.product_id as product_6_8_0__, mediaslist3_.id as id1_4_0__ from products products0_ inner join categories_products categories1_ on products0_.id=categories1_.product_id inner join categories categories2_ on categories1_.category_id=categories2_.id inner join medias mediaslist3_ on products0_.id=mediaslist3_.product_id
I am not sure what I do wrong the previous query is return 1000 rows from mysql and then it get the first 100 with the spring data but I need it to append limit 0,100 in the end of query, not sure how to do that
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!