Связано: Подсказка HINT_PASS_DISTINCT_THROUGH уменьшает количество сущностей, возвращаемых на страницу для запроса страницы, до значения ниже настроенного размера страницы (PostgreSQL)
Я настраиваю спецификацию JPA Реализация репозитория на основе, которая использует спецификации jpa (построенные на основе строк фильтра R SQL) для фильтрации результатов, определения порядка следования результатов и удаления любых дубликатов через «различные», которые в противном случае были бы возвращены из-за соединенных таблиц. Метод построителя спецификаций JPA объединяет несколько таблиц и устанавливает «отдельный» флаг:
public final class MySpec implements Specification<Tag>
{
@Override
public Predicate toPredicate(
final Root<Tag> root,
final CriteriaQuery<?> query,
final CriteriaBuilder builder)
{
final Join<Tag, Label> labelsJoin = root.join("labels", JoinType.INNER);
final Join<Label, LabelIdentity> labelIdentityJoin = labelsJoin.join("labelIdentity", JoinType.INNER);
final Predicate labelKeyPredicate = builder.equal(labelIdentityJoin.get("key"), property);
query.distinct(true);
return builder.and(
labelKeyPredicate,
builder.like(labelsJoin.get("value"), argument.replace('*', '%')));
}
}
Чтобы разрешить сортировку по столбцам объединенной таблицы, я применил подсказку «HINT_PASS_DISTINCT_THROUGH» к соответствующему методу репозитория (в противном случае, сортировка по столбцам объединенной таблицы возвращает ошибку по строкам «столбец сортировки должен быть включен в запрос SELECT DISTINCT»).
После этих изменений кажется, что фильтрация и сортировка работают должным образом. Тем не менее, подсказка, по-видимому, приводит к тому, что «отдельная» фильтрация применяется после того, как страница результатов уже построена, что уменьшает число возвращаемых объектов на странице с настроенного аргумента «Размер» PageRequest до того, что остается после того, как дубликаты отфильтрованы out.
Мой вопрос таков:
Можно ли устранить необходимость использовать разные (и, таким образом, решить проблему подкачки), каким-то образом повторно используя экземпляры Join
среди разных экземпляров Specification
? Например, создать экземпляры Join
и передать один и тот же экземпляр Join
в каждый новый экземпляр спецификации (например, через конструктор)?
Например, я попытался создать что-то вроде следующего, и затем передал этот экземпляр JoinCache
в каждый экземпляр Specification
, однако я получил ошибки о неправильном псевдониме, поэтому не уверен, что что-то подобное поддерживается даже?
public class JoinCache
{
private final CriteriaBuilder criteriaBuilder;
private final CriteriaQuery<Tag> criteriaQuery;
private final Root<Tag> tagRoot;
private final Join<Tag, Label> labelJoin;
private final Join<Label, LabelIdentity> labelIdentityJoin;
public JoinCache(final CriteriaBuilder criteriaBuilder)
{
this.criteriaBuilder = criteriaBuilder;
this.criteriaQuery = this.criteriaBuilder.createQuery(Tag.class);
this.tagRoot = criteriaQuery.from(Tag.class);
this.labelJoin = tagRoot.join("labels", JoinType.INNER);
this.labelIdentityJoin = labelJoin.join("labelIdentity", JoinType.INNER);
}
public Join<Tag, Label> getLabelJoin()
{
return labelJoin;
}
public Join<Label, LabelIdentity> getLabelIdentityJoin()
{
return labelIdentityJoin;
}
public CriteriaBuilder getCriteriaBuilder()
{
return criteriaBuilder;
}
public CriteriaQuery<Tag> getCriteriaQuery()
{
return criteriaQuery;
}
public Root<Tag> getTagRoot()
{
return tagRoot;
}
}
Обновление
Альтернативный подход с использованием подзапросов вместо объединений (таким образом, избегая необходимости использовать отличные вообще), однако я считаю, что порядок / сортировка в подзапросах не поддерживаются в спецификациях JPA:
https://hibernate.atlassian.net/browse/HHH-256
public class MySpec implements Specification<Tag>
{
@Override
public Predicate toPredicate(
final Root<Tag> root,
final CriteriaQuery<?> query,
final CriteriaBuilder builder)
{
final String argument = arguments.get(0);
final Subquery<Label> subQuery = query.subquery(Label.class);
final Root<Label> subRoot = subQuery.from(Label.class);
final Predicate tagPredicate = builder.equal(subRoot.get("tag"), root);
final Predicate labelKeyPredicate = builder.equal(subRoot.get("labelIdentity").get("key"), "owner");
subQuery.select(subRoot).where(tagPredicate, labelKeyPredicate, builder.like(subRoot.get("value"), argument.replace('*', '%'));
return builder.exists(subQuery);
}
}