I have a situation where I need to build a select distinct a.address from Person a
(where address is a Address entity inside the Person) kind of query.
I am using Specification to dynamically build my where clause and using the findAll(Specification<T>)
function to get the result. The problem is I can not use specification to build my select clause and hence can not use findAll(Spcification)
function.
What would be the best way to do something like this?
I came across the same issue, so in case it would help someone, this is what I did:
The Specification is being translated to the where clause, and the findAll(Specification<T>)
function is creating its own select clause. So there is no way we can fix this by somehow using the findAll(Specification<T>)
function.
I already had custom repository which extends SimpleJpaRepository
, so I've added a new method:
@Override
@Transactional(readOnly = true)
public List<Object> findDistinctValues(Specifications<T> spec, String columnName) {
return getQuery(spec, columnName).getResultList();
}
protected TypedQuery<Object> getQuery(Specification<T> spec, final String distinctColumnName) {
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Object> query = builder.createQuery(Object.class);
Root<T> root = applySpecificationToCriteria(spec, query);
if (null != distinctColumnName) {
query.distinct(true);
query.multiselect(root.get(distinctColumnName));
}
// We order by the distinct column, Asc
query.orderBy(builder.asc(root.get(distinctColumnName)));
return em.createQuery(query);
}
applySpecificationToCriteria
is in the SimpleJpaRepository
class.
Now you can use the findDistinctValues
method.
As it's the top question in google, I'll post the answer here.
In the specification you have access to query, so you can do
query.distinct(true);
Full example, which results in such SQL emitted:
2015-04-27 12:03:39 EEST [7766-759] postgres@sales LOG: execute : SELECT DISTINCT t1.ID, t1.NAME, t1.WEBNAME, t1.WEBORDER, t1.PVGROUPPARENT_ID, t1.SITE_ID FROM PRODUCTVARIANT t0, PVGROUP t1 WHERE ((t0.PRODUCTTYPE_ID = $1) AND (t0.PVGROUP_ID = t1.ID)) 2015-04-27 12:03:39 EEST [7766-760] postgres@sales DETAIL: parameters: $1 = '4608bdc9-d0f2-4230-82fd-b0f776dc2cfd'
public static Specification<PVGroup> byProductType(final ProductType pt) {
return (final Root<PVGroup> root, final CriteriaQuery<?> query, final CriteriaBuilder builder) -> {
query.distinct(true);
final CollectionJoin<PVGroup, ProductVariant> jPV = root.join(PVGroup_.productVariant);
final Path<ProductType> ptPath = jPV.get(ProductVariant_.productType);
return builder.equal(ptPath, pt);
};
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With