Reputation: 3566
I am trying to fetch the results of a group by query with a hibernate criteria. As you can see in the code below, I am adding lots of projections into a ProjectionList. I have a pagination in front-end so if the client wants to fetch 20 results, I'm setting firstResult
to 0
and maxResults
to 20
.
I need send total row count of this query, too. However, if I use setProjection(Projections.rowCount)
it will overwrite my groupby
, sum
, and max
projections.
How can I write something like select count(*) from ( myCriteria )
?
@Override
public SearchResult<ServiceIncomeSO> findServiceIncomesBySearch(IncomeQuery query, Pager pager) {
Criteria criteria = createCriteria(ServiceIncome.class, "sin");
criteria.createAlias("sin.partner", "ptr", JoinType.LEFT_OUTER_JOIN);
if (StringUtils.isNotBlank(query.getPartnerKey())) {
criteria.add(eq("ptr.key", query.getPartnerKey()));
}
if (StringUtils.isNotBlank(query.getPartnerTxt())) {
criteria.add(disjunction() //
.add(ilike("ptr.key", query.getPartnerTxt(), ANYWHERE)) //
.add(ilike("ptr.name", query.getPartnerTxt(), ANYWHERE)) //
.add(ilike("ptr.title", query.getPartnerTxt(), ANYWHERE))); //
}
if (CollectionUtils.isNotEmpty(query.getPartnerTypes())) {
criteria.createAlias("ptr.partnerTypes", "partnerType", JoinType.LEFT_OUTER_JOIN);
criteria.add(in("partnerType." + COLLECTION_ELEMENTS, query.getPartnerTypes()));
}
if (CollectionUtils.isNotEmpty(query.getCategories())) {
criteria.createAlias("ptr.categories", "cat", JoinType.LEFT_OUTER_JOIN);
criteria.add(in("cat.key", query.getCategories()));
}
if (query.getPeriodFromMonth() != null && query.getPeriodFromYear() != null) {
addPeriodFrom(criteria, "sin.year", "sin.month", query.getPeriodFromYear(), query.getPeriodFromMonth());
}
if (query.getPeriodToMonth() != null && query.getPeriodToYear() != null) {
addPeriodTo(criteria, "sin.year", "sin.month", query.getPeriodToYear(), query.getPeriodToMonth());
}
String[] fields;
ProjectionList projection;
if (query.getBreakout() == QueryBreakout.PARTNER) {
projection = Projections.projectionList() //
.add(Projections.groupProperty("sin.partner.key")) //
.add(Projections.groupProperty("sin.year")) //
.add(Projections.groupProperty("sin.month")) //
.add(Projections.sum("sin.subscriberCount")) //
.add(Projections.sum("sin.income"));//
fields = new String[] { "partnerKey", "year", "month", "subscriberCount", "income" };
} else {
projection = Projections.projectionList() //
.add(Projections.groupProperty("sin.partner.key")) //
.add(Projections.groupProperty("sin.service.id")) //
.add(Projections.groupProperty("sin.year")) //
.add(Projections.groupProperty("sin.month")) //
.add(Projections.max("sin.shortNumber")) //
.add(Projections.sum("sin.subscriberCount")) //
.add(Projections.sum("sin.income")) //
; //
fields = new String[] { "partnerKey", "serviceId", "year", "month", "shortNumber", "subscriberCount", "income" };
}
criteria.setProjection(projection);
criteria.addOrder(desc("sin.year")).addOrder(desc("sin.month"));
criteria.setFirstResult(pager.getOffset());
criteria.setMaxResults(pager.getMax());
List<ServiceIncomeSO> list = ((List<Object[]>) criteria.list()).stream() //
.map(values -> JavaLangUtils.setProperties(new ServiceIncomeSO(), fields, values)) //
.collect(Collectors.toList()); //
// TODO How to get count without retrieving all the data?
int totalResults = 0;
return new SearchResult<>(list, totalResults, pager.getMax(), pager.getOffset());
}
private void addPeriodFrom(Criteria criteria, String yearColumn, String periodColumn, int yearFrom, int periodFrom) {
criteria.add(disjunction() // OR
.add(and(eq(yearColumn, yearFrom), ge(periodColumn, periodFrom))) // year == yearFrom && period >= periodFrom
.add(gt(yearColumn, yearFrom))); // year > yearFrom
}
private void addPeriodTo(Criteria criteria, String yearColumn, String periodColumn, int yearFrom, int periodFrom) {
criteria.add(disjunction() // OR
.add(and(eq(yearColumn, yearFrom), le(periodColumn, periodFrom))) // year == yearFrom && period <= periodFrom
.add(lt(yearColumn, yearFrom))); // year < yearFrom
}
I can write the same query in plain SQL but cannot do it in Hibernate Criteria API.
Select Query:
SELECT
PARTNER_KEY,
SERVICE_ID,
INCOME_YEAR,
INCOME_MONTH,
SUM(SUBSCRIBER_COUNT),
SUM(INCOME)
FROM
PP_SERVICE_INCOME
WHERE
PARTNER_KEY = 'PART32143'
GROUP BY
PARTNER_KEY,
SERVICE_ID,
INCOME_YEAR,
INCOME_MONTH;
Select Result:
PARTNER_KEY SERVICE_ID INCOME_YEAR INCOME_MONTH SUM(SUBSCRIBER_COUNT) SUM(INCOME)
----------- ---------- ----------- ------------ --------------------- -----------
PART32143 1 2016 1 1234 175000
PART32143 1 2017 1 1234 175390
PART32143 1 2016 6 1234 151100
PART32143 1 2017 0 1234 157800
PART32143 1 2016 7 1234 175220
PART32143 1 2016 2 1234 143000
PART32143 1 2016 0 1234 150000
PART32143 1 2017 2 1234 143012
PART32143 1 2016 8 1234 143330
Count Query:
SELECT
COUNT(*)
FROM (
SELECT
PARTNER_KEY,
SERVICE_ID,
INCOME_YEAR,
INCOME_MONTH,
SUM(SUBSCRIBER_COUNT),
SUM(INCOME)
FROM
PP_SERVICE_INCOME
WHERE
PARTNER_KEY = 'PART32143'
GROUP BY
PARTNER_KEY,
SERVICE_ID,
INCOME_YEAR,
INCOME_MONTH
);
Count Result: 9
Upvotes: 0
Views: 2557
Reputation: 487
For this, first you do group by
using DetachedCriteria
like this,
DetachedCriteria dcrit = DetachedCriteria.forClass(Student.class);
dcrit.setProjection(Projections.projectionList()
.add(Projections.groupProperty("studentname").as("name")));
After grouping get the row count using criteria by adding the DetachedCriteria
as a subquery for the Criteria
like the following,
Criteria crit = sessionFactory.getCurrentSession().createCriteria(Student.class);
crit.add(Subqueries.propertyIn("id", dcrit));
crit.setProjection( Projections.rowCount() );
return ((Number) crit.uniqueResult()).intValue();
Upvotes: 3
Reputation: 21103
What you first want to do is instead of creating a Criteria
for your ServiceIncome
inner query, you want to create it as a DetachedCriteria
as follows:
DetachedCriteria myCriteria = DetachedCriteria.forClass(
ServiceIncome.class,
"sin"
);
Then you want to create your outer criteria like you normally would using the Session
and then specify the subquery as the DetachedCriteria
we specified above with the desired projection.
Criteria outerCriteria = session.createCriteria( ServiceIncome.class, "osin" );
outerCriteria.add( Subqueries.propertyIn( "id", myCriteria ) );
outerCriteria.setProjection( Projections.rowCount );
Upvotes: 1