@BatchSize умное или глупое использование?
Сначала я объясню, как я понял и использовать @BatchSize
: @BatchSize
сделано для того, чтобы загружать отношения объектов в пакетном режиме, делая меньше запросов SQL к базе данных. Это особенно полезно на LAZY @OneToMany
связи.
Однако это даже полезно на LAZY @OneToOne
отношение и @ManyToOne
: если вы загружаете список сущностей из базы данных и просите загрузить лентяй @*ToOne
сущность, он будет загружать сущности партиями, даже если я просто использую тест, который загружает отношение 1-й сущности списка.
Обратите внимание, если некоторые хотят тестировать: Это показывает, только если объекты еще не загружены: например, если у вас есть список пользователей с менеджером и список всех пользователей, когда вы получите доступ к менеджеру, ни один запрос не будет запущен, так как он уже загружен.
Единственный недостаток, который я вижу в этом методе, это то, что вы загружаете список элементов из базы данных, но используете только его часть. Это операция пост-фильтрации.
Итак, давайте перейдем к главному.
Давайте предположим, что я делаю все хорошо, чтобы никогда не выполнять операции, подобные постфильтрации, даже если это заставляет меня выполнять собственные запросы SQL или использовать объекты DTO для запроса критериев множественного выбора и так далее.
- Правильно ли я считаю, что могу просто
@BatchSize
все ленивые отношения после тщательного обдумывания использования энергичной загрузки / соединения и, наконец, выбора ленивых отношений? - Есть ли у меня какой-либо интерес для поиска адекватного значения для
@BatchSize
или я могу думать "чем больше, тем лучше"? Это будет означать, "есть ли предел числа в операторе SQL IN, который может сделать мой запрос достаточно медленным, чтобы он больше не стоил? Я использую Postgres, но если у вас есть ответы на другие вопросы SGBD, я тоже заинтересован. - Дополнительный вопрос: кажется, что использование
@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 ответ
- Да,
@BatchSize
предназначен для использования с ленивыми ассоциациями. - В любом случае Hibernate выполнит несколько операторов в большинстве ситуаций, даже если число неинициализированных прокси / коллекций меньше указанного размера пакета. Смотрите этот ответ для более подробной информации. Кроме того, более легкие запросы по сравнению с менее крупными могут положительно повлиять на общую пропускную способность системы.
@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
, что приводит к большему количеству запросов, но с гораздо меньшим общим объемом данных, которые передаются из базы данных. Кроме того, отдельные запросы намного проще, чем большой запрос с объединениями, и их проще выполнять и оптимизировать для базы данных.