Hibernate: Joining Criteria - java

Hi I need to do the following using Criteria
Select * from product pd, (Select productID pd1 from ... where ...) tpd1,
(Select productID pd2 from ... where ...) tpd2
where pd.productID = tpd1.pd1 and pd.productID = tpd1.pd2
May I know if it is possible?
The original SQL was using IN conditions
Select * from product pd where productID in (Select productID pd1 from ... where ...) and
productID in (Select productID pd2 from ... where ...)
but it takes too long to get the result, using the join SQL statement I was able to obtain my result faster.
any help?

Given you're using Hibernate, you may have to do something like this, which should work ok if the number of expected matches are relatively low:
select *
from product pd
where pd.productID in
(select productID
from product pd2
join tpd1 on (pd2.productID = tpd1.pd1)
join tpd2 on (pd2.productID = tpd2.pd2)
where tpd1....
and tpd2....
);
I assume there is a unique index on product.productID.
Alternatively, you could try the EXISTS formulation which may or may not work better than your original query:
select *
from product pd
where EXISTS
(select null from tpd1 where pd.productID = tpd1.pd1 and ...)
and EXISTS
(select null from tpd2 where pd.productID = tpd2.pd2 and ...)
;

Related

JPA Criteria API: select count(*) from subquery with joins and group by for pagination implementation

I want to implement a pagination feature in a list of products returned from database. So I need to known the total results of the query.
Database tables are:
PRODUCT TABLE
-----------------------------
ID NAME
-------------- --------------
1 Product 1
2 Product 2
PRODUCT SUPPLIER TABLE
--------------------------------------------
ID PRODUCT ID PRICE
-------------- -------------- --------------
1 1 35
2 1 30
3 2 70
4 2 75
The desire result is a list of products with the minimum price of the different suppliers.
Product 1 30
Product 2 70
Main query should be something like:
select p.name, min(ps.price) from product
inner join product_supplier ps on ps.product_id = p.id
group by p.name
So count query should be something like:
select count(*) from (
select p.name, min(ps.price) from product
inner join product_supplier ps on ps.product_id = p.id
group by p.name
)
Code I'am using to get paginated results is:
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Tuple> tupleQuery = cb.createTupleQuery();
Root<product> product = tupleQuery.from(product.class);
Join<product, productSupplier> productSuppliers = product.join("productSuppliers");
tupleQuery.multiselect(
product,
cb.min(productSuppliers.get("price")).alias("price")
);
tupleQuery.groupBy(product);
tupleQuery.orderBy(cb.asc(product.get("name")));
TypedQuery<Tuple> query = entityManager
.createQuery(tupleQuery)
.setMaxResults(pageable.getPageSize())
.setFirstResult(Long.valueOf(pageable.getOffset()).intValue());
List<Tuple> products = query.getResultList();
for (Tuple productResult : products) {
Product product = productResult.get(product);
Double minPrice = (Double)productResult.get("price");
ProductDTO productDTO = new ProductDTO(product, minPrice);
}
But I'm not sure how to get count from the main query. Code I'm using is:
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Long> countQuery = cb.createQuery(Long.class)
Root<Product> product = countQuery.from(Product.class);
Join<Product, ProductSupplier> productSuppliers = product.join("productSuppliers");
countQuery.groupBy(product);
countQuery.select(cb.count(product));
long count = entityManager.createQuery(countQuery).getSingleResult();
But it produces the following SQL:
select
count(product0_.id) as col_0_0_
from
pu_products product0_
inner join
pu_product_suppliers productsu1_
on product0_.id=productsu1_.product_id
group by
product0_.id
Returning more than one result, throwing the following exception:
org.springframework.dao.IncorrectResultSizeDataAccessException: query did not return a unique result: 31
So I don't know how to get it work.
Thanks.
Seems that is not possible to do a select count (*) from from an aggregated query via Criteria API:
Discussion in discourse.hibernate.org
You can use a subquery only in your WHERE but not in the SELECT or FROM clause
So finally I've applied this solution found in this answer:
After build the query via Criteria API, get the final query string.
Wrap it with select count(*) from ({final_query_string}).
Create a Native Query with the result of wrapping the final query string with the select count (*) from.
Full code of this solution in this answer from #fliX.
Regards.
What about a namedquery like this one :
select count(distinct ps.product) from product_supplier ps where ps.price is not null

QueryDSL - order by count as alias

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.

Criteria Builder select from select

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.

complicated sql query returning empty result

My database looks like this
tickets table
-------------
ticket_id
title
description
department_id
status_id
priority_id
assignee_id
creator_id
departments table
------------------
dep_id
dep_name
status table
------------
status_id
status_name
priority table
---------------
pr_id
pr_name
users table
-----------
u_id
username
password
salt
email
firstName
lastName
department_id
userlevel_id
userlevels table
-----------------
ul_id
ul_name
I need to load all tickets given the asignee id. My query looks like this
SQLQuery q = q.createSQLQuery("SELECT t.*,d.*,s.*,p.*,u.*,a.* FROM tickets t, departments d, status s, priority p, users u, attachments a WHERE t.department_id=d.dep_id AND t.status_id=s.stat_id AND t.priority_id=p.pr_id AND t.assignee_id=u.u_id AND t.creator_id=u.u_id AND t.tick_id=a.ticket_id AND assignee_id=?");
q.setInt(0, some_valid_assignee_id);
List<Object> result = q.list();
But it's returning an empty object list. Can anyone point me in the right direction, thanks!!
In your query you have AND t.assignee_id = u.u_idand also AND t.creator_id = u.u_id. The only way that would return any records is if the assignee_id and creator_id were the same. I think what you really need to do is link to the users table twice like so:
SELECT t.*, d.*, s.*, p.*, u1.*, u2.*, a.*
FROM tickets t
INNER JOIN departments d ON t.department_id = d.dep_id
INNER JOIN status s ON t.status_id = s.stat_id
INNER JOIN priority p ON t.priority_id = p.pr_id
INNER JOIN users u1 ON t.assignee_id = u.u_id
INNER JOIN users u2 ON t.creator_id = u.u_id
INNER JOIN attachments a ON t.tick_id = a.ticket_id
WHERE assignee_id = ?
You should use proper join syntax . . . makes the query easier to read and to write.
However, I'm guessing that the problem is:
t.assignee_id = u.u_id AND t.creator_id = u.u_id
That would assume that
t.assignee_id = t.creator_id
Perhaps this never happens in your data.

MySQL Select values in table depending on another table

I have a table 'Staff' with details of all Staff. 'StaffID' included.
Then I have a table 'StaffRole' which has 'StaffID' and 'RoleID'
I want to do something like this: Select * From Staff Where RoleID=1;
But I'm not sure if I can do this as RoleID is not in the Staff table.
Anyone know the correct syntax? Any feedback is appreciated.
Try this:
SELECT * FROM staff s
WHERE EXISTS(
SELECT 'ROLE'
FROM staffrole r
WHERE r.staffid = s.staffid
AND r.roleid = 1
)
In alternative:
SELECT * FROM staff s
JOIN staffrole r
ON r.staffid = s.staffid
WHERE r.roleid = 1
Use a join:
select s.*
from staffrole r
join staff s
on s.staffid = r.staffid
where roleid = 1
An index on staffrole(roleid) should make it perform better.

Categories