Ну и опять же, причина может быть не только в LOB-ах, а и в большом количестве полей, например, которые редко меняются. Частая ситуация, когда у сущности меняется только статус - например заказ в интернет магазине, или количество, как например товар в розничной торговой точке. А если учесть, что таких операций может быть несколько тысяч в секунду, то выигрыш приличный получается.
И ещё надо учитывать, что БД одна, а приложений тысячи, так что проще вытащить весь объём из БД сразу, чем бомбить её бедную-несчастную лишними запросами.
>Любая Lazy-загрузка -- это потенциальная N+1 проблема.
А если этих полей несколько, то будет ещё "веселее".
>fetch = FetchType.EAGER` вы же над коллекциями не ставите,
Потому что при работе с коллекциями возникают проблемы при join-ах. Для скалярных свойств нет таких проблем.
Более того, в ManyToOne EAGER по умолчанию, а это неспроста.
>Как вы работаете с lazy-коллекциями "за пределами транзакций"?
Ну да, мало гемора с коллекциями, обретаем проблемы для скалярных свойств
С коллекциями Lazy используется не только и не столько из-за уменьшения объёма, но и по другим причинам.
>А внимательно перечитаете мой самый первый комментарий, погуглите -- уверен, заработает.
Ещё раз говорю, что если вынести в отдельную таблицу, то все проблемы исчезают сами собой без плагинов и наследования. Причём в оракле, например, даже предпочтительней хранить LOB-ы в отдельной таблицы, легче их удалять.
Хочу подытожить, что универсальных решений не бывает и утверждать, что DynamicUpdate - это всегда плохо, по крайней мере неправильно.
Равно как и Lazy загрузка может быть полезной в некоторых случаях
Опять же, с Lazy имеем кучу проблем, таких как n+1 и работа за пределами транзакций. Тем более Lazy для скалярных свойств без танцев с бубнами не особо работает. Вот пример для h2:
>Вы предлагаете @DynamicUpdate , а я предлагаю сделать его LAZY
Lazy - это не панацея. Если это поле таки прочиталось, то получите бесполезное обновление (а обновление LOB-ов это дорогостоящая операция). Если вынести а отдельную таблицу, то этих проблем избегаем. Но опять же, статья не про проектирование.
>ссылка на маленький тест демонстрирующий разницу между обычным обновлением и динамическим, была бы куда более весом
Могу даже большой тест привести). Например магазины "Магнит" работают в том числе благодаря такой оптимизации. Что может быть весомей? )
>Во-первых, в MVCC базах скорее пишется новое состояние записи целиком
Все примеры проверялись на Oracle и Postgres. Писались только изменённый поля.
>Во-вторых, "дефолтный" update-запрос по всем полям кешируется, а динамические будут каждый раз парсится
Современные СУБД решают эту проблему
>Если не записывать обновленный LOB -- тогда и читать его не над
Странное утверждение. Читают данные не с целью потом их записать. Проблему с записью LOB конечно можно решить переносом их в отдельную таблицу, но данная статья не про проектирование БД.
>Отслеживание изменений полей -- dirty checking -- происходит всегда
Я обратного и не утверждал. dirty checking упоминался в другом разделе, просьба внимательнее читать статью
>но в БД на него повешен триггер и происходит какой-то side effect.
Триггерная логика - это зло, там более если она приводит к побочкам)
>В общем, без измерений под нагрузкой от @DynamicUpdate скорее всего будет псевдо-радость от "оптимизированных" SQL-запросов
Я описал ситуации, когда данная технология будет уместна, равно как и проблемы. Опять же, просьба внимательнее читать статью. Ещё хочу сказать, что все примеры взяты из реальной жизни и я лично встречался с ситуациями, когда @DynamicUpdate резко повышал производительность.
Далеко не всегда SQL запросы занимают одну строчку
При использовании db-specific native query при переключении с СУБД на СУБД (что очень актуально в наше время) придётся править java код, в случае же сохранения запросов в xml ресурсах переключение делается на уровне конфигов.
Статья как раз для тех, кто не хочет писать ручками, а хочет пользоваться плодами чужого труда. В программировании это благо)
запускать кодогенерацию на каждый билд
Как минимум, нужно запускать при изменении версии API. А если команд, поставляющих каждая свой API, штук десять-двадцать, то это не совсем удобно.
всего api на 100500 методов
Проектируйте API грамотно, и будет вам счастье)
держать в памяти ненужные классы.
Если таки прочитать всю статью, а не только выводы, то будет понятно, что библиотека API содержит только интерфейсы и потребителю совсем нет необходимости загружать все их реализации. Так что тут экономия на спичках. Но опять же, если команда-поставщик грамотно проектирует API.
Ха-ха-ха.
Что может быть проще для потребителя, чем поменять номер версии в gradle? При желании, чтобы получить актуальную версию API, можно даже это действие убрать.
Ну и опять же, причина может быть не только в LOB-ах, а и в большом количестве полей, например, которые редко меняются. Частая ситуация, когда у сущности меняется только статус - например заказ в интернет магазине, или количество, как например товар в розничной торговой точке. А если учесть, что таких операций может быть несколько тысяч в секунду, то выигрыш приличный получается.
И ещё надо учитывать, что БД одна, а приложений тысячи, так что проще вытащить весь объём из БД сразу, чем бомбить её бедную-несчастную лишними запросами.
А в целом, спасибо за совет, конечно. Я не юзал подобный подход, возьму на заметку
>Любая Lazy-загрузка -- это потенциальная N+1 проблема.
А если этих полей несколько, то будет ещё "веселее".
>fetch = FetchType.EAGER` вы же над коллекциями не ставите,
Потому что при работе с коллекциями возникают проблемы при join-ах. Для скалярных свойств нет таких проблем.
Более того, в ManyToOne EAGER по умолчанию, а это неспроста.
>Как вы работаете с lazy-коллекциями "за пределами транзакций"?
Ну да, мало гемора с коллекциями, обретаем проблемы для скалярных свойств
С коллекциями Lazy используется не только и не столько из-за уменьшения объёма, но и по другим причинам.
>А внимательно перечитаете мой самый первый комментарий, погуглите -- уверен, заработает.
Ещё раз говорю, что если вынести в отдельную таблицу, то все проблемы исчезают сами собой без плагинов и наследования. Причём в оракле, например, даже предпочтительней хранить LOB-ы в отдельной таблицы, легче их удалять.
Хочу подытожить, что универсальных решений не бывает и утверждать, что DynamicUpdate - это всегда плохо, по крайней мере неправильно.
Равно как и Lazy загрузка может быть полезной в некоторых случаях
Опять же, с Lazy имеем кучу проблем, таких как n+1 и работа за пределами транзакций. Тем более Lazy для скалярных свойств без танцев с бубнами не особо работает. Вот пример для h2:
@Column(name = "LOB")@Lob @Basic(fetch = FetchType.LAZY)private String lob;
При чтении получаем:
"select bt1_0."id",bt1_0."LOB",bt1_0."NAME" from "BIG_TABLE" bt1_0"
Видно, что никакой ленивой загрузки нет
>Вы предлагаете
@DynamicUpdate
, а я предлагаю сделать егоLAZY
Lazy - это не панацея. Если это поле таки прочиталось, то получите бесполезное обновление (а обновление LOB-ов это дорогостоящая операция). Если вынести а отдельную таблицу, то этих проблем избегаем. Но опять же, статья не про проектирование.
>ссылка на маленький тест демонстрирующий разницу между обычным обновлением и динамическим, была бы куда более весом
Могу даже большой тест привести). Например магазины "Магнит" работают в том числе благодаря такой оптимизации. Что может быть весомей? )
>Гм, раз вы меня дважды упрекаете, что я невнимательно читаю статью
Да, невнимательно.
>Тем не менее, в статье есть
Ко вот только это относится не DynamicUpdate, а к Stateless Session. Просьба таки прочитать статью целиком)
>Это очень смелое утверждение. Можете, пожалуйста, привести аргументы/доказательства?
Почитайте Тома Кайта, например. Там всё популярно описано
>Во-первых, в MVCC базах скорее пишется новое состояние записи целиком
Все примеры проверялись на Oracle и Postgres. Писались только изменённый поля.
>Во-вторых, "дефолтный" update-запрос по всем полям кешируется, а динамические будут каждый раз парсится
Современные СУБД решают эту проблему
>Если не записывать обновленный LOB -- тогда и читать его не над
Странное утверждение. Читают данные не с целью потом их записать. Проблему с записью LOB конечно можно решить переносом их в отдельную таблицу, но данная статья не про проектирование БД.
>Отслеживание изменений полей -- dirty checking -- происходит всегда
Я обратного и не утверждал. dirty checking упоминался в другом разделе, просьба внимательнее читать статью
>но в БД на него повешен триггер и происходит какой-то side effect.
Триггерная логика - это зло, там более если она приводит к побочкам)
>В общем, без измерений под нагрузкой от
@DynamicUpdate
скорее всего будет псевдо-радость от "оптимизированных" SQL-запросовЯ описал ситуации, когда данная технология будет уместна, равно как и проблемы. Опять же, просьба внимательнее читать статью. Ещё хочу сказать, что все примеры взяты из реальной жизни и я лично встречался с ситуациями, когда @DynamicUpdate резко повышал производительность.
На 5-й версии SUBSELECT работал, но не работала пагинаци.
Да, все примеры для spring boot 3.
Если не указать
@BatchSize, то в Postgres всё считается в коллекцию, потом получите стрим. В Oracle нет такого. Проверял на разных версиях
Работает без проблем
Всё бы так, но есть пара нюансов
Далеко не всегда SQL запросы занимают одну строчку
При использовании db-specific native query при переключении с СУБД на СУБД (что очень актуально в наше время) придётся править java код, в случае же сохранения запросов в xml ресурсах переключение делается на уровне конфигов.
Сергей, спасибо! Отличная идея и реализация!
Статья как раз для тех, кто не хочет писать ручками, а хочет пользоваться плодами чужого труда. В программировании это благо)
Как минимум, нужно запускать при изменении версии API. А если команд, поставляющих каждая свой API, штук десять-двадцать, то это не совсем удобно.
Проектируйте API грамотно, и будет вам счастье)
Если таки прочитать всю статью, а не только выводы, то будет понятно, что библиотека API содержит только интерфейсы и потребителю совсем нет необходимости загружать все их реализации. Так что тут экономия на спичках. Но опять же, если команда-поставщик грамотно проектирует API.
Что может быть проще для потребителя, чем поменять номер версии в gradle? При желании, чтобы получить актуальную версию API, можно даже это действие убрать.