Я пытаюсь создать пользовательскую группу и агрегировать API, в котором пользователь может группировать по ряду параметров в любой комбинации.
Мой класс проекции подобен
public class ShipmentAggregate {
private ShipmentMode mode;
private ShipmentStatus status;
private Long count;
private Double weight;
public static ShipmentAggregate getEmptyAggregate() {
return ShipmentAggregate.builder().count(0l).weight(Double.valueOf(0)).build();
}
}
Myрепозиторий похож на
public List<ShipmentAggregate> getAggregate(ShipmentSpecification spec, Set<String> groupBy) {
final CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
final CriteriaQuery<ShipmentAggregate> query =
criteriaBuilder.createQuery(ShipmentAggregate.class);
final Root<Shipment> root = query.from(Shipment.class);
if (groupBy != null) {
List<Path<?>> grouping = getGroupingExpressions(root, groupBy);
List<Selection<?>> selectionList = getSelection(criteriaBuilder, root, grouping);
query
.multiselect(selectionList)
.where(spec.toPredicate(root, query, criteriaBuilder))
.groupBy(grouping.toArray(new Expression<?>[grouping.size()]));
return entityManager.createQuery(query).getResultList();
}
return List.of(ShipmentAggregate.getEmptyAggregate());
}
private List<Selection<?>> getSelection(
final CriteriaBuilder criteriaBuilder, final Root<Shipment> root, List<Path<?>> grouping) {
List<Selection<?>> selectionList = new ArrayList<>();
selectionList.addAll(grouping.stream().map(Selection.class::cast).collect(Collectors.toList()));
selectionList.add(criteriaBuilder.count(root).alias(FIELD_COUNT));
selectionList.add(
criteriaBuilder.coalesce(criteriaBuilder.sum(root.get("grossWeight")), 0).alias("weight"));
return selectionList;
}
private List<Path<?>> getGroupingExpressions(Root<Shipment> root, Set<String> groupBy) {
List<Path<?>> expressions = new ArrayList<>();
groupBy.forEach(item -> expressions.addAll(findExpression(root, item)));
return expressions;
}
private List<Path<?>> findExpression(Root<Shipment> root, String item) {
switch (item) {
case "mode":
return List.of(root.get("mode"));
case "status":
return List.of(root.get("status"));
}
// TODO throw exception
return null;
}
Если API получает всю группу по полям, он работает, но если по крайней мере один отсутствует, я получаю Невозможно найти подходящее исключение конструктора, так как он пытается создать список результатовиспользуя конструктор.(например, когда groupBy=mode
только, но конструктор также содержит status
), как правильно отобразить этот динамический выбор, или есть ли способ получить Tuple
в качестве результата и сопоставить столбцы (я не устанавливаю псевдонимы)для динамических столбцов)?