Спецификация JPA + динамическая сортировка для поля многие ко многим без дубликатов - PullRequest
0 голосов
/ 26 ноября 2018

У меня есть класс с множеством полей, отношением «многие к одному» и «многие ко многим», и мне нужно добавить динамические фильтры по некоторым столбцам (из этого класса и по полям из подключенного класса) и добавить сортировку поа также эти поля.

У меня проблема с фильтрацией и сортировкой по полю "многие ко многим"

@Entity
class EntityA {
...
@ManyToMany
@JoinTable (
        name = "EntityA_EntityB",
        joinColumns = { @JoinColumn(name = "EntityB") },
        inverseJoinColumns = { @JoinColumn(name = "entityb_id") }
)
private List<EntityB> bEntities;
...
}

И у меня есть спецификация для фильтрации EntityA по EntityB.name (я установил критерииQuery.distinct (true) для предотвращения дубликатов, которые у меня есть без этого)

public class EntityASpecifications {
//other specifications
...

public static Specification<EntityA> entityBNameContains(String query) {
    return (root, criteriaQuery, criteriaBuilder) -> {

        if (query == null) {
            return criteriaBuilder.conjunction();
        }
        criteriaQuery.distinct(true);

        return getContainsPredicate(criteriaBuilder, root.join("bEntities").get("name"), query);
    };
}

private static Predicate getContainsPredicate(CriteriaBuilder criteriaBuilder, Expression<String> field, String query) {

    return (query == null) ? criteriaBuilder.conjunction() : criteriaBuilder.like(criteriaBuilder.lower(field), getContainsPattern(query));
}

private static String getContainsPattern(String searchTerm) {

    return (searchTerm.isEmpty()) ? "%" : "%" + searchTerm.toLowerCase() + "%";
}
}

Работает нормально, проблема в том, что когда я пытаюсь использовать сортировку и этот фильтр одновременно

entityARepository.findAll(EntityASpecifications.entityBNameContains("name"), PageRequest.of(page, size, Sort.Direction.ASC, sortColumnName));

Сбой для полей, подключенных к EntityA как EntityB.name (также у меня есть некоторые другие поля с @ManyToOne, которые не работают) со следующим Исключением:

Caused by: com.microsoft.sqlserver.jdbc.SQLServerException: ORDER BY items must appear in the select list if SELECT DISTINCT is specified

Если я удаляю critQuery.distinct(правда);все будет хорошо, но у меня будут дубликаты, и я не хочу, чтобы они были

Как это исправить и не иметь дублированных результатов одновременно?

1 Ответ

0 голосов
/ 30 ноября 2018

Причина, по которой вы получаете повторяющиеся результаты для EntityA, заключается в том, что вы присоединяетесь к EntityA с EntityB, а ваш предикат основан на полях в EntityB.Таким образом, если есть несколько записей EntityB, которые удовлетворяют вашему условию, и все они принадлежат одному и тому же EntityA, вы получите несколько записей EntityA.Таким образом, решение состоит в том, чтобы использовать «существует» вместо объединения двух таблиц, и вам больше не нужно будет использовать разные.Ваша спецификация может выглядеть так:

 public class EntityASpecifications {
 //other specifications
 ...

   public static Specification<EntityA> entityBNameContains(String query) {
       return (root, criteriaQuery, criteriaBuilder) -> {

          if (query == null) {
              return criteriaBuilder.conjunction();
          }
          Subquery< EntityB> subquery = query.subquery(EntityB.class);
          Root< EntityB> subqueryRoot = subquery.from(EntityB.class);
          subquery.select(subqueryRoot);

          subquery.where(criteriaBuilder.and(criteriaBuilder.equal(root, subqueryRoot.get("entitya_id")),
                                criteriaBuilder.like(criteriaBuilder.lower("name"), getContainsPattern(query)))
                   );

          return criteriaBuilder.exists(subquery);

       };
   } 
   private static String getContainsPattern(String searchTerm) {
     return (searchTerm.isEmpty()) ? "%" : "%" + searchTerm.toLowerCase() + "%";
 }
} 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...