Заказать по счету с помощью Spring Data JpaRepository

Я использую Spring Data JpaRepository, и я считаю его чрезвычайно простым в использовании. Мне действительно нужны все эти функции - пейджинг, сортировка, фильтрация. К сожалению, есть одна маленькая неприятная вещь, которая, кажется, заставляет меня вернуться к использованию простой JPA.

Мне нужно заказать размер связанного сбора. Например, у меня есть:

@Entity
public class A{
    @Id
    private long id;
    @OneToMany
    private List<B> bes;
//boilerplate
}

и я должен сортировать по bes.size()

Есть ли способ каким-то образом настроить заказ, используя преимущества разбивки на страницы, фильтрацию и другие Spring отличные функции Data?

Ответ 1

Я решил головоломку, используя подсказки и вдохновения:

Первая и самая важная вещь, о которой я не знал о заключается в том, что даже используя @Query настраиваемые методы, все же можно создать запросы подкачки, просто передав объект Pageable в качестве параметра. Это то, что может быть явно указано документацией spring -data, поскольку это определенно не очевидно, хотя очень мощная функция.

Отлично, теперь вторая проблема - как я сортирую результаты по размеру связанной коллекции в JPA? Мне удалось перейти к следующему JPQL:

select new package.AwithBCount(count(b.id) as bCount,c) from A a join a.bes b group by a

где AwithBCount - это класс, который на самом деле отображает результаты запроса:

public class AwithBCount{
    private Long bCount;
    private A a;

    public AwithBCount(Long bCount, A a){
        this.bCount = bCount;
        this.a = a;
    }
    //getters
}

Возмутилось, что теперь я могу просто определить свой репозиторий, как показано ниже

public interface ARepository extends JpaRepository<A, Long> {
    @Query(
        value = "select new package.AwithBCount(count(b.id) as bCount,c) from A a join a.bes b group by a",
        countQuery = "select count(a) from A a"
    )
    Page<AwithBCount> findAllWithBCount(Pageable pageable);
}

Я поспешил попробовать свое решение. Perfect - страница возвращается, но когда я пытался сортировать по bCount, я разочаровался. Оказалось, что поскольку это ARepository (не репозиторий AwithBCount), то spring -data попытается найти свойство bCount в вместо AwithBCount. Итак, наконец, я получил три пользовательских метода:

public interface ARepository extends JpaRepository<A, Long> {
    @Query(
        value = "select new package.AwithBCount(count(b.id) as bCount,c) from A a join a.bes b group by a",
        countQuery = "select count(a) from A a"
    )
    Page<AwithBCount> findAllWithBCount(Pageable pageable);

    @Query(
        value = "select new package.AwithBCount(count(b.id) as bCount,c) from A a join a.bes b group by a order by bCount asc",
        countQuery = "select count(a) from A a"
    )
    Page<AwithBCount> findAllWithBCountOrderByCountAsc(Pageable pageable);

    @Query(
        value = "select new package.AwithBCount(count(b.id) as bCount,c) from A a join a.bes b group by a order by bCount desc",
        countQuery = "select count(a) from A a"
    )
    Page<AwithBCount> findAllWithBCountOrderByCountDesc(Pageable pageable);
}

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

Ответ 2

Один из вариантов, который намного проще, чем исходное решение и который также имеет дополнительные преимущества, заключается в создании представления базы данных совокупных данных и привязки вашего объекта к этому с помощью @SecondaryTable или @OneToOne.

Например:

create view a_summary_view as
select
   a_id as id, 
   count(*) as b_count, 
   sum(value) as b_total, 
   max(some_date) as last_b_date 
from b 

Используя @SecondaryTable

@Entity
@Table
@SecondaryTable(name = "a_summary_view", 
       pkJoinColumns = {@PrimaryKeyJoinColumn(name = "id", referencedColumnName= "id")})
public class A{

   @Column(table = "a_summary_view")
   private Integer bCount;

   @Column(table = "a_summary_view")
   private BigDecimal bTotal;

   @Column(table = "a_summary_view")
   private Date lastBDate;
}

Теперь вы можете сортировать, фильтровать, запрашивать и т.д. исключительно со ссылкой на объект A.

В качестве дополнительного преимущества у вас есть данные вашей модели домена, которые могут быть дорогостоящими для вычисления в памяти, например. общая стоимость всех заказов для клиента без необходимости загрузки всех заказов или возврата к отдельному запросу.

Ответ 3

Я мало что знаю о Spring Data, но для JPQL, для сортировки объектов по размеру связанной коллекции, вы можете использовать запрос

Select a from A a order by a.bes.size desc