'CriteriaQuery DISTINCT values in an aggregation function
Introduction
Consider a query like the following:
SELECT
building.name AS building_name,
STRING_AGG(DISTINCT visit.visitor_name, ', ') AS visitors,
COUNT(visit.id) AS total_visits
FROM building
LEFT JOIN visit
ON building.id = visit.building_id
GROUP BY building.id
Note the DISTINCT keyword used in STRING_AGG aggregation function. The results could look something like the following:
+----------------------+-------------+--------------+
| building_name | visitors | total_visits |
+----------------------+-------------+--------------+
| Skyline Residence | Edgar, John | 6 |
| Forbidden Residence | | 0 |
| Cloud Nine Residence | John | 1 |
+----------------------+-------------+--------------+
The important part is that if John and Edgar have visited Skyline Residence 6 times, then the visitors column should not display their names multiple times like John, Edgar, Edgar, Edgar, John, John.
JPA CriteriaQuery Code
This is my JPA CriteriaQuery code so far, without DISTINCT:
CriteriaBuilder builder = getEntityManager().getCriteriaBuilder();
CriteriaQuery<ResultDto> query = builder.createQuery(ResultDto.class);
Root<Building> building = query.from(Building.class);
Join<Building, Visit> visit = building.join("visits", JoinType.LEFT);
query.select(builder.construct(
ResultDto.class,
building.get("name"),
builder.function("STRING_AGG", String.class, visit.get("visitorName"), builder.literal(", ")),
builder.count(visit.get("id")) // ^ I need DISTINCT here ^
));
query.groupBy(building.get("id"));
final TypedQuery<ResultDto> typedQuery = getEntityManager().createQuery(query);
final List<ResultDto> resultList = typedQuery.getResultList();
How could I modify this code to generate the above SQL query, with DISTINCT?
Note: Do not answer with Hibernate's deprecated Criteria API solutions. It should be done using JPA's CriteriaQuery.
Solution 1:[1]
You can write a template for string_agg using MetadataBuilderContributor
public class SqlFunctionsMetadataBuilderContributor implements MetadataBuilderContributor {
@Override
public void contribute(MetadataBuilder metadataBuilder) {
metadataBuilder.applySqlFunction(
"string_agg_distinct",
new SQLFunctionTemplate(StandardBasicTypes.STRING, "string_agg(distinct ?1, '; ')")
);
}
}
And add in the properties:
spring.jpa.properties.hibernate.metadata_builder_contributor=<your-package>.SqlFunctionsMetadataBuilderContributor
And then use "string_agg_distinct" instead of "STRING_AGG" in function
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Dorin |
