Reputation: 131
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?
Upvotes: 13
Views: 41125
Reputation: 1250
Will this work ?
List<Person> findDistinctPeopleByAddress(String lastname, String firstname);
followed by iterating thru the list and using Person.getAddress() ?
Upvotes: -2
Reputation: 679
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);
};
}
}
Upvotes: 10
Reputation: 2583
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.
Upvotes: 6
Reputation: 16050
A quick and dirty to solution is to filter the result using a Set
:
Set<...> set = new HashSet<...>( findAll( ... ) )
and be sure that equals()
and hashCode()
are relevantly implemented on the domain class :-)
Cheers,
Upvotes: 3
Reputation: 9935
Good! You can use distinct
in JPQL
and also for specific column. It is already here.
Reference Using DISTINCT in JPA
Upvotes: 4