Обновить

Комментарии 11

Один запрос, нет N+1, загружается полный граф.

Если дочерние сущности замаплены как Set, а не как List, то запрос вполне может быть только один, на самом деле. Только за hashCode и equals надо будет ещё очень аккуратно следить.

А так да, определённая проблема, действительно, существует.

Полностью с Вами согласен.

На практике многие команды вообще не усложняют решение подобных задач и ограничиваются маппингом только необходимых полей из persistence-модели во view-модель.

Для большого числа сценариев этого вполне достаточно.

Очень полезная статья

jdbc, jooq, blaze… А зачем, зачем тянуть дополнительные зависимости (ну, кроме jdbc)?

Почему бы Criteria API не использовать? Код бы почти не отличался от jdbc, но оставался бы в рамках JPA.

А если упороться, то можно написать стримовских коллекторов, получать из запроса стрим результатов, а его уже собирать.

  1. По поводу Criteria API.

    Статья Thorben Janssen - "Создавайте более эффективные запросы Criteria с помощью механизма сохранения данных Blaze".

    Цитата - "К сожалению, Criteria API от JPA не очень популярен, поскольку его сложно читать и писать".

    Ссылка - https://thorben-janssen.com/create-better-criteria-queries-with-blaze-persistence/

    Цитата из поста на линкдине Thorben Janssen - "There’s one thing I don’t like in Hibernate / JPA… and that’s the Criteria API!".

    Можно найти через поиск гугл.

Статья Влад Михалча - Blaze Persistence – "Лучший способ написания запросов JPA Criteria Queries".

Ссылка - https://vladmihalcea.com/blaze-persistence-jpa-criteria-queries/

2. По поводу стримовских коллекторов.

Это тот же JDBC-подход из статьи, просто в функциональном стиле — архитектурно ничего не меняется. Реального стримингового чтения всё равно не будет, драйвер буферизует весь ResultSet. А вложенные коллекторы для трёх уровней читаются хуже, чем явный цикл! Те же яйца только в профиль.

3. По поводу зависимостей.

А вы в папочку с зависимостями когда-то заглядывали? Видели сколько их? Там уже Spring, Hibernate, Jackson и ещё полсотни транзитивных. На фоне этого jOOQ или Blaze — просто капля в море. Аргумент “не хочу лишних зависимостей” работает, только если вы пишете на чистом Java SE без ничего.

Если есть аргументация Ваших допущений, то буду раз ознакомиться!

0

  1. Вкусовщина и аппеляция к авторитету, не убедительно.

  2. Я так и сказал, просто код будет более однородным, т.к. написан в одном стиле.

Реального стримингового чтения всё равно не будет, драйвер буферизует весь ResultSet

Просто готовить правильно надо. Конечно, зависит от драйвера, но оракловый и постгресовый точно так не делают. Проверено неоднократно.

  1. Лучший код - тот, которого нет. Если можно обойтись без чего-либо, то нужно обходиться без этого. Зависимость - это не только лишнее место на диске, это ещё и баги, техдолг с обновлением и совместимостью.

По пункту 1. Окей, авторитеты не аргумент. Тогда конкретно: что именно вам нравится в Criteria API? Многословность? Необходимость писать cb.equal(root.get(“name”), “value”) вместо читаемого условия? Если есть практический кейс, где Criteria API выигрывает у альтернатив, покажите, обсудим.

По пункту 2. “Однородность стиля” это аргумент про эстетику, не про инженерию. Если задача собрать вложенный граф из плоского ResultSet, то вопрос не в стиле, а в читаемости и сопровождаемости конкретного кода. Вложенные коллекторы для трёх уровней вложенности, покажите рабочий пример, который читается лучше явного цикла. Буду рад ошибиться.

По поводу буферизации, драйвер PostgreSQL по умолчанию буферизует весь ResultSet, если не выставить fetchSize и не открыть транзакцию явно. Это не моё утверждение, это документация. Если у вас другой опыт, какой именно fetchSize, какая транзакция, какой объём данных?

По пункту 3. “Лучший код тот, которого нет” это принцип YAGNI, и он хорош против оверинжиниринга. Но здесь он применён наоборот: вы предлагаете писать руками то, что инструмент делает надёжнее и декларативнее. Получается больше кода, но своего. Это не минимализм, это NIH-синдром.

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

что именно вам нравится в Criteria API? Многословность?

Во-первых, я нигде не говорил, что мне он нравится? Да, он многословен, но он “уже есть”, иметь инструмент и не использовать его - глупо. Во-вторых, Criteria API - это крайний случай, когда не получается выразить запрос через самописный аналог спринговых спецификаций работающих с наследниками jakarta.persistence.metamodel.Attribute. Т.е. в коде у меня будет repo.find(SpecificationBuilder.eq(Entity_.name, “Name”)) и т.п. Внутри это, естественно, всё так или иначе разворачивается в Criterai API. Но это сделано давно, протестировано и не вызывает ни у кого никаких вопросов.

не выставить fetchSize и не открыть транзакцию явно

А если запрос неправильно написать, то можно вообще не те данные получить. В чём проблема этот fetchSize выставить? А транзакция так или иначе тем же спрингом будет открыта/закрыта.

какой именно fetchSize

Зависит от ситуации, может быть и 50, и 100, и 1000.

какая транзакция

Не понял вопроса: есть и readOnly, и полноценные с потоковыым изменением сущностей.

какой объём данных?

Разный, и десятки, и сотни тысяч и миллионы. Тут главное либо чистить кэш первого уровня, либо не тягать сущности полностью, если нужны пара полей.

предлагаете писать руками

Представьте себе, это часть моей работы.

инструмент делает надёжнее и декларативнее

Чтобы сделать select e from Entity e where e.attribute1 = ?1 and e.attribute2 > ?2 мне декларативность не нужна. А написать пару-тройку запросов руками - это куда надёжнее, чем новую тащить зависимость, у которой могут быть транзитивные завиcимости со своими багами и которой ещё надо научиться правильно пользоваться, т.к. в ней тоже могут быть нюансы.

Получается больше кода, но своего

Кода получается не больше, а ровно столько, сколько нужно. И без лишней шелухи.

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

Опять же не надо придумывать то, чего я не говорил. Я разве сказал где-то о вложенных коллекторах? Я говорил о коллекторах в общем, для каждого случая - свой. И читаться он будет ничуть не хуже, чем цикл. Т.к. по-сути, всё, что находится за телом цикла станет полями коллектора, а тело цикла - телом аккумулятора.

  1. По поводу Criteria API.

    Статья Thorben Janssen - "Создавайте более эффективные запросы Criteria с помощью механизма сохранения данных Blaze".

    Цитата - "К сожалению, Criteria API от JPA не очень популярен, поскольку его сложно читать и писать".

    Ссылка - https://thorben-janssen.com/create-better-criteria-queries-with-blaze-persistence/

    Цитата из поста на линкдине Thorben Janssen - "There’s one thing I don’t like in Hibernate / JPA… and that’s the Criteria API!".

    Можно найти через поиск гугл.

Статья Влад Михалча - Blaze Persistence – "Лучший способ написания запросов JPA Criteria Queries".

Ссылка - https://vladmihalcea.com/blaze-persistence-jpa-criteria-queries/

2. По поводу стримовских коллекторов.

Это тот же JDBC-подход из статьи, просто в функциональном стиле — архитектурно ничего не меняется. Реального стримингового чтения всё равно не будет, драйвер буферизует весь ResultSet. А вложенные коллекторы для трёх уровней читаются хуже, чем явный цикл! Те же яйца только в профиль.

3. По поводу зависимостей.

А вы в папочку с зависимостями когда-то заглядывали? Видели сколько их? Там уже Spring, Hibernate, Jackson и ещё полсотни транзитивных. На фоне этого jOOQ или Blaze — просто капля в море. Аргумент “не хочу лишних зависимостей” работает, только если вы пишете на чистом Java SE без ничего.

Если есть аргументация Ваших допущений, то буду раз ознакомиться!

Вы не туда ответили.

@aleksandy

Спасибо!

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации