@BatchSize умное или глупое использование?

Сначала я объясню, как я понял и использовать @BatchSize: @BatchSize сделано для того, чтобы загружать отношения объектов в пакетном режиме, делая меньше запросов SQL к базе данных. Это особенно полезно на LAZY @OneToMany связи.

Однако это даже полезно на LAZY @OneToOne отношение и @ManyToOne: если вы загружаете список сущностей из базы данных и просите загрузить лентяй @*ToOne сущность, он будет загружать сущности партиями, даже если я просто использую тест, который загружает отношение 1-й сущности списка.

Обратите внимание, если некоторые хотят тестировать: Это показывает, только если объекты еще не загружены: например, если у вас есть список пользователей с менеджером и список всех пользователей, когда вы получите доступ к менеджеру, ни один запрос не будет запущен, так как он уже загружен.

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

Итак, давайте перейдем к главному.

Давайте предположим, что я делаю все хорошо, чтобы никогда не выполнять операции, подобные постфильтрации, даже если это заставляет меня выполнять собственные запросы SQL или использовать объекты DTO для запроса критериев множественного выбора и так далее.

  1. Правильно ли я считаю, что могу просто @BatchSize все ленивые отношения после тщательного обдумывания использования энергичной загрузки / соединения и, наконец, выбора ленивых отношений?
  2. Есть ли у меня какой-либо интерес для поиска адекватного значения для @BatchSize или я могу думать "чем больше, тем лучше"? Это будет означать, "есть ли предел числа в операторе SQL IN, который может сделать мой запрос достаточно медленным, чтобы он больше не стоил? Я использую Postgres, но если у вас есть ответы на другие вопросы SGBD, я тоже заинтересован.
  3. Дополнительный вопрос: кажется, что использование @BatchSize в классе не дает много результатов. Я все еще должен аннотировать все ленивые отношения, я что-то пропустил или это бесполезно?

РЕДАКТИРОВАТЬ: Смысл моей 3 в том, что я получаю другое поведение.

Допустим, я загружаю список объектов класса "A", который имеет отношение LAZY OneToMany к B. Теперь я хочу напечатать все creationDate of B. Итак, я делаю классический цикл 2 for.

Я аннотировал B с BatchSize сейчас:

  • @OneToMany не аннотируется с помощью BatchSize: каждый набор B загружается на каждой итерации независимо без пакетирования. Так что моя аннотация по классу B, похоже, полностью игнорируется. Даже если я установлю значение "два" и у меня будет 6 записей в одном наборе, у меня будет один запрос для этого набора.
    • @OneToMany аннотирован: у меня есть определенный запрос загруженных пакетов. Если я установлю размер пакета на два, и у меня будет в общей сложности 10 B, я получу только 5 запросов: независимо от количества A, которое у меня есть. Если я установлю его на 100: у меня есть 1 запрос для объектов B.

PS: я не рассматриваю какой-либо связанный запрос к B, который может сработать, чтобы загрузить поля B с выборкой select/subselect.

РЕДАКТИРОВАТЬ 2: я только что нашел этот пост Почему бы мне не использовать @BatchSize на каждом лениво загруженных отношений? Хотя я гуглил и искал на SO, прежде чем задавать свой вопрос, думаю, я не использовал правильные слова...

Однако я добавляю что-то другое, что может привести к другому ответу: когда я задаюсь вопросом об использовании BatchSize для всех отношений, это после выбора, хочу ли я загружать с нетерпением, с выборкой соединения / выбора или если я хочу отложенной загрузки.

1 ответ

Решение
  1. Да, @BatchSize предназначен для использования с ленивыми ассоциациями.
  2. В любом случае Hibernate выполнит несколько операторов в большинстве ситуаций, даже если число неинициализированных прокси / коллекций меньше указанного размера пакета. Смотрите этот ответ для более подробной информации. Кроме того, более легкие запросы по сравнению с менее крупными могут положительно повлиять на общую пропускную способность системы.
  3. @BatchSize на уровне класса означает, что указанный размер пакета для объекта будет применяться для всех @*ToOne ленивые ассоциации с этим лицом. Смотрите пример с Person юридическое лицо в документации.

Связанный вопрос / ответы, которые вы предоставили, больше заботятся о необходимости оптимизации и ленивой загрузки в целом. Они применимы и здесь, конечно, но они не относятся только к пакетной загрузке, что является лишь одним из возможных подходов.

Еще одна важная вещь связана с активной загрузкой, о которой упоминается в связанных ответах, и которая предполагает, что если свойство всегда используется, то вы можете получить более высокую производительность с помощью активной загрузки. Это вообще не верно для коллекций и во многих ситуациях для индивидуальных ассоциаций.

Например, предположим, что у вас есть следующий объект, для которого bs а также cs всегда используются, когда A используется.

public class A {
  @OneToMany
  private Collection<B> bs;

  @OneToMany
  private Collection<C> cs;
}

Жадно загружается bs а также cs очевидно, страдает от проблемы выбора N+1, если вы не объединяете их в одном запросе. Но если вы присоединитесь к ним в одном запросе, например:

select a from A
  left join fetch a.bs
  left join fetch a.cs

затем вы создаете полный декартово произведение между bs а также cs и возвращаясь count(a.bs) x count(a.cs) строки в наборе результатов для каждогоa которые читаются один за другим и собираются в сущности A и их коллекции bs а также cs,

Пакетная загрузка будет очень оптимальной в этой ситуации, потому что вы сначала прочитаете Aс, тогда bs а потом cs, что приводит к большему количеству запросов, но с гораздо меньшим общим объемом данных, которые передаются из базы данных. Кроме того, отдельные запросы намного проще, чем большой запрос с объединениями, и их проще выполнять и оптимизировать для базы данных.

Другие вопросы по тегам