Reputation: 273
I have a JPA entity User which contains a field (entity) City. I want to select one page of, for example, 10 users but from different cities.
In SQL I would use something like:
SELECT DISTINCT ON (u.city_id) u.username ,u.email, u.city_id ....
FROM user u LIMIT 0,10 ....
but I need to do it with JPQL or JPA criteria builder. How can I achieve this?
Upvotes: 6
Views: 3810
Reputation: 164
Recently I came across same situation, found that there is no direct way using criteria query to support it.
Here is my solution -
- Create custom sql function for distinct on
- register function to dialect
- Update dialect in properties
- call it from criteria query
1) Create Custom function
public class DistinctOn implements SQLFunction {
@Override
public boolean hasArguments() {
return true;
}
@Override
public boolean hasParenthesesIfNoArguments() {
return true;
}
@Override
public Type getReturnType(Type type, Mapping mapping) throws QueryException {
return StandardBasicTypes.STRING;
}
@Override
public String render(Type type, List arguments, SessionFactoryImplementor sessionFactoryImplementor) throws QueryException {
if (arguments.size() == 0) {
throw new QueryException("distinct on should have at least one argument");
}
String commaSeparatedArgs = String.join(",",arguments);
return " DISTINCT ON( " + commaSeparatedArgs + ") " + arguments.get(0) + " ";
}
}
2) Register Function
public class CustomPostgresSqlDialect extends PostgreSQLDialect {
public CustomPostgresSqlDialect() {
super();
registerFunction("DISTINCT_ON", new DistinctOn());
}
}
3) Update Dialect : Here pass on your class name
spring.jpa.properties.hibernate.dialect = com.harshal.common.CustomPostgresSqlDialect
4) Use it in Criteria Query
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<User> query = cb.createQuery(User.class);
Root<User> user = query.from(User.class);
// SELECT DISTINCT ON (u.city_id) u.username
query.multiselect(
cb.function("DISTINCT_ON", String.class, user.get("city")),
user.get("userName")
);
return em.createQuery(query).getResultList();
Upvotes: 4
Reputation: 95
You can do this by using Hibernate Criteria Query
sample code can be like this
Criteria criteria = session.createCriteria(user.class);
ProjectionList projectionList = Projections.projectionList();
projectionList.add(Projections.distinct(projectionList.add(Projections.property("city_id"), "cityId")));
projectionList.add(Projections.property("username"), "username");
projectionList.add(Projections.property("email"), "email");
criteria.setProjection(projectionList2);
criteria.setResultTransformer(Transformers.aliasToBean(user.class));
List list = criteria.list();
Upvotes: 0