Pull to refresh

Comments 196

Не сработает. Даже на простейших примерах, которые успешно ORM успешно разруливает, ваша концепция ломается.

where id in (что-то из другой таблички)

select ... inner join другая табличка

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

Тоже об этом задумался когда переводил. Но это Егор Бугаенко) Его мнение почти никогда не совпадает с мнением комьюнити)

Я, если честно, удивлён, что кто-то по собственной инициативе вбросы Бугаенко переводит.

Егор отлично видит проблемы. Но его решения не годятся. Но проблема-то есть.

То, что SQL противоречит ООП - факт.

То, что SQL противоречит ООП - факт.

Это искажение из разряда "желаемое за действительное". А если их не противопоставлять и не исходить из подразумеваемого долженствования (что SQL должен соотноситься/подчиняться ООП) то ни отрицать ни доказывать ничего не нужно будет. Теплое останется теплым, а мягкое будет только помогать теплому не остывать так быстро. Как плед. Во! Плед - не противоречит ООП!

Кстати, почему когда обсуждают ORM - все напрочь игнорируют всякие микро-ORM.

В мире .NET его офигенный Linq2DB - он, кмк, отлично решает все проблемы:

  1. Разработчик хочет удобный DSL, чтобы делать запросы к базе. Желательно, без просачивания SQL в код.

  2. Разработчик не хочет портить красивый мир его объектного дизайна, подстраивать модель под фреймворк.

  3. Разработчик не хочет терять в производительности только из-за того что его ORM не может что-то, что можно сделать прямым запросом к базе.

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

Этот самый слой можно будет легко и безопасно мокать, в отличие от DbContext/SessionFactory/etc.
А тестировать соединение с БД без БД просто нереально, что многие пытаются делать при помощи всяких in-memory sqlite, EF Core InMemory, или H2, заменяя настоящий продовый Postgres или что там ещё, а потом ловят какие-нибудь ошибки от того, что какая-то фича работает в проде, но не работает в тесте, или наоборот.

Описанные вами проблемы - от недостатка документации, компетенций и смекалки.

До него проблему заметили некто Мартин Фаулер и Jeff Atwood.

Согласен. Её многие видят, но пока никто не решил.

Проблема, описанная в статье, вообще к БД никакого отношения не имеет. Это просто проблема альтернативы по размещению кода внутри классов. Мы можем делать либо

class Отчет {
    Записать отчет в базу данных(...);
    Вывести отчет на принтер(...);
}
class Заказ {
    Записать заказ в базу данных(...);
    Вывести заказ на принтер(...);
}

либо

class База данных {
    Записать отчет в базу данных(...);
    Записать заказ в базу данных();
}
class Принтер {
    Вывести отчет на принтер(...);
    Вывести заказ на принтер(...);
}

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

«В вашей работе много нового и верного. К сожалению, то, что в ней верно, – то неново, а то, что ново, – то неверно»

Это же выбор программиста, использовать sql или нет. Или я не вижу каких-то деталей?

Сработает, у меня есть проектик, там или очень простенькие инсерты/апдейты, или ветвистые селекты но только для чтения. В итоге создаём ворох объектов для селектов, пишем этот разухабистый код и все работает. Для некоторых совсем веселых селектов с кучей параметров и опциональными пересечениями привлекается QueryBuilder.

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

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

Вам никто не мешает самому писать SQL в хибернейте

Так нафига тогда хибернейт?

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

Как минимум, он приводит sql-типы к предопределённой объектной модели, а это обычно много бойлер-плейт кода.

Так нафига тогда хибернейт?

ради миграций?
пока не видел миграций, которыми хотелось бы пользоваться, вне orm

Предполагается, что, например, делается некий интерфейс PostsWithUsers, у которого и будут методы типа GetUsersWitchAnswerPost(postId). И под капотом там уже будет sql с where / join. Либо, если действительно сложная выборка, то делаются интерфейсы для построителя запросов типа IQueryable. Которые внутри сервиса "разматываются" и по ним строится sql. Так что всё это делается, вопрос только в том, стоит ли такое дикое усложнение ради "строгости" ООП.

В интерфейсе Posts создаете метод GetPostsByбизнес-условие. В реализации PgPosts РУЧКАМИ с помощью jdbc реализуете выборку из таблицы. Что не сработает?

ОРМ (и JPA) типа облегчает процесс работы для простых выборок и CRUD. Для сложных выборок из нескольких таблиц с кучей условий (что и является предметом работы энтерпрайз-программиста), hibernat или jpa превращается в срань, намного худшую, чем работа с читым SQL. Потому что SQL - всем известный стандарт, а всякие HQL-ли - самодельные птичьи языки, которые ТИПА упрощают работы с sql. Плюс всякие сраные глюки от хиберната, вроде недавнего (и не знаю, пофиксенный ли), когда первая выборка работает нормально, а при всех последующих приложение падает.

Единственный плюс hibernat - одинаковость HQL для разных баз, SQL может незначительно различаться.

"We don't care which you choose, just don't choose an ORM" (c) Jake Wharton, Google & Alec Strong, Square, Inc.

А можно я просто на SQL писать буду? Без всего этого лишнего обвеса.

А как вы потом будете применять дополнительные изменения к запросу? Например у вас есть некоторый метод который подготавливает базовый запрос с условиями. Затем где то ещё потребовалось к этому запросу добавить ещё условий вынеся старые в отдельный OR и добавить группировку.

Или под каждую задачу запрос с нуля писать будете?

Уж лучше с нуля, чем Орм.

Если запрсы совсем большие и сложные, то проще написать хранимку/функцию.

Проблема в ловле дефектов и разбора происходящего в эксплуатации. Явно написанный SQL можно просто взять из кода, и выполнить (попросить выполнить тех людей, что продукт эксплуатируют) на боевой (или близкой к ней) базе, чтобы посмотреть, что там оно такое из базы прочитало. А вот с тем, что ORM построил - надо включать отладку для вывода этих запросов, потом продираться через дебри этого отладочного вывода, собирая только то, что имеет отношение к делу итд итп. В результате время на получение ответа 'что такого произошло, что мы имеем не то поведение, которое хотели' резко увеличивается.

Или же можно взять нормальные инструменты для отладки и увидеть всё то же самое независимо от способа построения запроса.

Нельзя. Потому что эксплуатация - она где-то там, у заказчика. И с инструментами для отладки тебя туда не пустят.

А с сырыми запросами что, пускают?..

Ну как... Говоришь "хочу результат такого запроса", они на него смотрят, решают, что он ничего плохого не сделает, выполняют.

Потом в результате затирают все, что тебе не положено видеть, присылают результат тебе. Далее цикл повторяется.

Невозможно вписать все варианты SQL в код в общем случае, так как часто нужно комбинировать условия, выбираемые данные и т.п. Это приводит к комбинаторному взрыву количество вариантов SQL запросов, который решается только генерацией. В итоге либо генерирует ORM или необъектный генератор или наколенное решение.

Есть ещё шаблонизация

…которая обладает всеми перечисленными выше дефектами ORM.

Да наверное, особенно учитывая что чего-то такого готового большого и проверенного нету (только dbt может быть, но это про другое)

ORM не дает полного контроля над запросами и делает написание и отлаживание нетривиальных запросов намного сложнее.

Вот есть у меня колонка в Postgis типа geometry. А мне нужно вызвать на ней функцию, которая работает с geography, но так чтобы индекс использовался. Я иду, создаю функциональный индекс, а потом в запросе делаю каст типа column::geography. Все, работает, збс.

А ORM (привет sqlalchemy) мне не дает этот каст сделать. Делает более сложный каст, с параметрами, который индекс не использует.

Это только пример одной ОРМ. А у них у всех свои приколы.

А какой процент от всех запросов в приложении нетривиальные? Можно использовать orm для простых запросов, избавив кодовую базу от рутинного ручного CRUDа, а в тех редких случаях, когда это действительно имеет смысл, писать голый sql.

В Джаве для этого есть Spring Data JDBC. Те же автоматическая десериализация, готовый круд, декларативные транзакции и тд, но запросы пишутся руками. Не отбирает контроль, облегчает жизнь

Но большинству макак слишком сложно, проще 100+ аннотаций для JPA навесить на энтити и потом месяц пытаться понять, почему все это работает не так, как ожидалось

Я сейчас работаю на проекте с 2млн строк кода и Hibernate. Насмотрелся на эти гирлянды на всю жизнь. И знаете, когда разница времени выполнения между запросом от хибера и ручным составляет 700 раз, немного задумываешься

Собственно, многие люди и тут, и в крупных компаниях солидарны с моим отношением к ОРМ (достаточно посмотреть на начало этого треда)

Но для фанатиков всегда "не любит мое любимое = ниасилил"

Ну а вы не думаете, что это проблема хибернейт?

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

Возможно. У меня другие платформы встречаются только в петах, а там ORM я не использую

Через месяц дойдут руки запилить бэк к пету на Rust, там хочу потыкать хваленую кодогенерацию времени компиляции ORM Diesel, посмотрю на их реализацию

Но в целом, и из хибернейта можно выжать приемлемый SQL, почти всегда. Но зачастую это потребует такой возни с конфигами и жонглирования аннотациями, что десять раз бы уже успел руками написать этот запрос нормально и пойти дальше. И даже в таких случаях результат все равно в 3-5 раз медленнее голого jdbc (у меня получалось в лучшем случае 43с jdbc vs 4,5 минуты hibernate на большом инсерте вложенных объектов в бд)

Плюс, если бы все было так локально, то и поливали грязью бы только хибер, а не все ORM в целом. Я почему-то уверен, что если копнуть поглубже, то и в EF найдутся проблемы

Вот у меня прошлый проект был тем самым кровавым энтерпрайзом. С лютым легаси и тд. И вот там бд была оракл, на 200 000 таблиц и 80 000 хранимых процедур. Запросы писались руками и занимали 5-8 экранов сплошного текста. Я бы скорее сдох от старости, чем заставил бы ORM сгенерировать хоть что-то рабочее на таком объеме данных. И что-то мне подсказывает, что ORM тут могла быть любая, ничего от этого не изменилось бы

Я уже молчу про хинты запросов для планировщика, которые есть в Оракле и, с экстеншеном, в Постгрессе. Что-то я сомневаюсь, что тут тоже можно сгенерировать что-то адекватное по сравнению с тем, что знающий проект человек напишет ручками

Diesel мне не нравится (напоминает Slick на Scala), но это пока самый рабочий вариант на Rust. Есть еще Sqlx, но там пока отсутствуют некоторые необходимые на мой взгляд вещи, типа batch insert.

Для меня идеальный инструмент - что-то типа Doobie на Scala.

На Скале не писал, ничего сказать не могу.

На Расте у меня это будет первый проект по сути, поэтому тоже все только в будущем. Долго выбирал между Actix и Rocket, остановился на первом в итоге. Не понравилось отношение разработчиков Rocket к проекту и полный провал по перфомансу по сравнению с конкурентом. А у Actix из коробки интеграция с Diesel, вот на него выбор и распространился, т.к. альтернатив, по сути, и нет

Абсолютно согласен про Rocket. Автор изначально решил сделать максимально "удобно" в ущерб производительности, в надежде потом просто оптимизировать, добавить асинхронность и тд.

Actix - хороший выбор.

Да там даже с удобством проблемы. Они только-только пересели на стейбл версию Раста. И до сих пор вебсокеты, одна из основных фич любого бэкенда, отложены аж на 3 мажорные версии вперед. А автор рекомендует параллельно Rocket запускать просто второй сервер (сторонний, с поддержкой вебсокетов) и организовывать взаимодействие между ними, типа решение (что мешает просто изначально взять не Rocket, а то, где есть вебсокеты?). Есть большие вопросы к тому, кто рулит проектом и строит его роадмапу

Вот у меня прошлый проект был тем самым кровавым энтерпрайзом. С лютым легаси и тд. И вот там бд была оракл, на 200 000 таблиц и 80 000 хранимых процедур. Запросы писались руками и занимали 5-8 экранов сплошного текста. Я бы скорее сдох от старости, чем заставил бы ORM сгенерировать хоть что-то рабочее на таком объеме данных. И что-то мне подсказывает, что ORM тут могла быть любая, ничего от этого не изменилось бы

Ну вы понимаете, что если бизнес логика написана на хранимках в БД, то ОРМ тут не поможет. Тут даже из названия очевидно, что object relational mapping не про хранимки на 5 экранов

 И даже в таких случаях результат все равно в 3-5 раз медленнее голого jdbc (у меня получалось в лучшем случае 43с jdbc vs 4,5 минуты hibernate на большом инсерте вложенных объектов в бд)

Очевидно что bulk вставки в БД быстрее сингл инсертов, да еще и с проверкой вложенных объектов? Что вы хотите доказать?

Плюс, если бы все было так локально, то и поливали грязью бы только хибер, а не все ORM в целом. Я почему-то уверен, что если копнуть поглубже, то и в EF найдутся проблемы

В EF есть проблемы, безусловно. Но для той же балк инсерта или апдейта есть Entity Framework Extensions (платные, но можно найти и бесплатные пакеты) которые практически равны скорости голому SQL. Очевидно, что ORM с кэшированием и трэкингом объектов в общем случае медленнее, чем голый сиквел, но если писать так, как надо писать, то скорость отличается процентов на 20, а не в 4 раза.

А если нужен действительно сложный запрос, то всегда можно смапить на DTO хранимку.

Тут даже из названия очевидно, что object relational mapping не про хранимки на 5 экранов

Смешались в кучу кони, люди...

С чего вы взяли, что там хранимки на 5 экранов? Это 5 экранов чистого SQL, хранимки вообще отдельно. Мне казалось, это очевидно из сообщения

Очевидно что bulk вставки в БД быстрее сингл инсертов, да еще и с проверкой вложенных объектов? Что вы хотите доказать?

И вот опять. Можно ткнуть пальцем, где было хоть слово про сингл инсерты?

Наоборот, я явно указал, что оптимизировал как только можно. Там был батчинг хибернейта настроен, плюс опытным путем было выяснено, что сами листы с объектами лучше тоже бить на пачки, а не пихать разом. Потому что хибернейт-то их разобьет на пакеты нужного размера, но вот кэши у него при этом перегреются это всё держать

но если писать так, как надо писать, то скорость отличается процентов на 20, а не в 4 раза.

А если отвечать на то, что писали, то можно узнать, что люди вокруг - не идиоты, которые наляпали кое-как и бегут жаловаться на хабр, а заранее оптимизировали всё и рассказывают итоговые результаты)

Если у вас одиночный запрос 5 экранов занял в итоге — то как средствами какого-нибудь Linq2DB его генерировать гораздо проще чем писать в сыром виде. Просто из-за возможности растащить части запроса по промежуточным переменным, которой в сыром SQL нету.

Проблемы начинаются на слове "генерировать" в сочетании в фразой "запрос на 5 экранов"

Кроме того, опять напоминаю про хинты, которые я пока ни в одном выхлопе генератора не видел

Я и не говорил про идиотов.

Каждая проблема имеет свое решение. Запросы на 5 страниц - это сама по себе проблема, ORM тут вообще непричем. Не стоит удивляться, что написать вменяемый запрос на 5 страниц ОРМ не может. Не его эта задача, как я и писал. А вы приципились к хранимкам.

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

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

Так что мое мнение не изменилось. Нормальный ORM - это наипрекраснейший инструмент для работы с небольшими операциями в БД - изменение вставка отдельных записей, простенькие джойны. Мой опыт говорит, что 95% работы по объему - именно такие кейсы и ОРМ заметно упрощает жизнь. И ОРМ - это зона ответственности разработчика бэка.

В то же время, любые действительно сложные выборки, батч инсерты или делиты должны писаться профессиональным DBD, так как там на самом деле куча всяких особенностей, например как табличная блокировка при большом количестве одновременных удалений или различные планы запросов потому что статистика поменялась. Соответственно бэкэндер лишь использует этот код от DBD, и для этого в ОРМ тоже есть возможности.

Так а тривиальные запросы тривиально и на SQL пишуться.

Проблема в том, что тривиального crud только в туториалах много. А в реальном приложении всяких join, динамичных запросов и т п. дофига. Писать на hibernate все это - боль (в сравнении с sql).

А для тривиального crud всегда можно на том же jooq абстракцию написать.

Да нет, не тривиально они пишутся. Одно только перечисление всех колонок таблицы способно сократить жизнь пишущему (а не перечислять нельзя — при изменении схемы всё развалится). А ещё результат запроса надо разобрать и куда-то записать...

jOOQ смего генератором эти проблемы решает довольно-таки легко.

jOOQ — это нечто промежуточное между полноценными ORM и чистым SQL. Я бы его не торопился записывать в лагерь чистого SQL.

С хорошей IDE (а-ля datagrip) это не проблема

Любая известная мне IDE от Jetbrains замечательно с этим справлялась с помощью подсказок. Пишешь имя первого поля, она подсказывает остальные. И дает два варианта - с id и без, для инсерта

А потом это все саппортить будет? А потом у нас испортозамещение и отказ от Oracle и все хранимки переписывать, к примеру на по PostgreSQL Dialect.

Вот например в dbt это решается моделями (грубо говоря это sql-запрос), которые можно шаблонизировать и переиспользовать

Мне больше нравится подход SQLDelight с мультиплатформы Котлина и JOOQ из Джавы. Когда разработчик руками пишет SQL-запросы, а библиотека по ним и миграциям генерирует энтити и методы в репозиториях для вызова этих запросов. Получается "ORM наоборот", который и не отбирает контроль, и не генерирует всякую хрень (чем славится тот же Hibernate), и при этом облегчает работу

У Егора Бугаенко часто свой взгляд на программиррвание как таковое. Он может быть противоречивым, неявным, но никогда не скучным) Всегда слушаю его с интересом, и жду следующей лекции под названием "ООП - зло, нас ждет тернистый путь обратно к процедурному программированию"))

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

UFO just landed and posted this here

Опять надо выступать в стиле "Карл Маркс и Фридрих Энгельс — это не муж и жена, а четыре разных человека". В смысле, ООП, процедурное, функциональное и декларативное — это совершенно разные языки, очень плохо выразимые друг через друга.

>но никогда не скучным)
Ну, может вам и нескушно, я верю. Обычно целью желтой прессы (а тут именно она) является как раз развлечь. Но когда большинство концепций либо ничем не подтверждены, либо разваливаются при первой же попытке проверить практикой — это называется балабол, а не «свой взгляд на программирование». Взгляд все же предполагает некоторое исследование, доказательства и т.п. И если мне пишут «ORM зло», я как минимум ожидаю, что будут приведены явные и несомненные плюсы ORM — потому что иначе это автоматически пустопорожняя болтовня, а всех остальных (кто успешно применяет) автор считает идиотами. Я же предпочитаю подход, что если кто-то применяет технологию в приложении (а таких людей много), то априори им лучше знать про свои приложения, а идиот как раз вероятно автор утверждения.

Ну вот вам пример: «Нет никаких оправданий существованию ORM в любом приложении». Простите, но с каких пор Бугаенко разрешили утверждать за любое приложение? Мои приложения он в глаза никогда не видел, и не знает, приносит ли мне ORM пользу, или же вред. Поэтому данный взгляд идет лесом, а применять ли ORM, какую именно, и какую пользу я собираюсь получить — я уж решу сам.

>«ООП — зло, нас ждет тернистый путь обратно к процедурному программированию»))
Ну да, чисто ради поржать я бы тоже на такое посмотрел бы. Но этот жанр же называется «клоунада», не так ли?

Но когда большинство концепций либо ничем не подтверждены, либо разваливаются при первой же попытке проверить практикой — это называется балабол, а не «свой взгляд на программирование».

Тут вы как раз в корне неправы. Егор практикующий программист, он пишет кода больше чем вся ваша команда, скорее всего. Посмотрите на его проекты на гитхабе, какой там код (не библиотеки типа cactoos, там как раз странновато, а прикладные проекты вроде zold и других). Там код на голову лучше чем в любом проекте на spring boot с ORM. Свой взгляд он как раз доказал практически много раз.

>Егор практикующий программист, он пишет кода больше чем вся ваша команда, скорее всего.
Заметьте, это — такое же точно ни на чем не основанное утверждение, как и «ORM зло». Еще раз уточню — у меня претензия не к тому, что ORM бывает лишним звеном, у меня претензия к попытке обобщить это на все проекты, включая например мой (которого ни вы ни он в глаза не видели).

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

Ложь в том, что ORM якобы зло всегда. Если вы пытаясь доказать это утверждение, и не способны (а точнее даже не пытаетесь) сформулировать ни одного плюса ORM — я почти на 100% уверен, что тут налицо подтасовка.

Соглашусь с вами. Сам не сторонник ORM, он зачастую вреден в больших проектах. Запросы тормозят, а узкое место - базы данных. Если при большой нагрузке серваков можно наклонировать и запараллелить, то с базами это делается намного сложнее.

Небольшие проекты с терпимыми нагрузками - прекрасно ложится ORM. Быстрое прототипирование, десятки/сотни CRUD-ов и вот рабочее приложение готово. Тяп-ляп готово.

Когда приложение большое, а данные приложения потом использует аналитический отдел, когда тебе нужно строить многоэтажные запросы, которые выдадут пользователю сложный набор данных, то никакой ORM не сравнится по производительности правильно написанному запросу, который потом идеально адаптирует планировщик запросов СУБД.

Могу ошибаться, но на своей практике, чаще всего, когда я встречаю ярых поклонников ORM, то часто так оказывается, что они просто не понимают СУБД, их плюсов, им нет особо дела до производительности (ведь это дело девопсов, пусть просто добавят серваков если тормозит), не хочется загрязнять код SQL-ем (тут можно понять), или им просто лень писать запросы (тоже понимаю, лень - двигатель прогресса, но везде нужен баланс).

Ну да, в какой-то степени все это имеет место. Но заметьте, разница между вашим утверждением «зачастую вреден в больших проектах» и утверждением «ORM зло» — весьма существенная.

мой (которого ни вы ни он в глаза не видели). 

Вот именно что не видели. Дайте ссылку на гитхаб, и мы сравним, какой подход лучше. Иначе это просто балабольство, мол есть где-то какой-то мифический код, в котором очень удобно и красиво применяется ORM. Дайте ссылку

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

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

Кто он вообще такой, чтобы его слушать? Первый раз просто услышал, а люди судя по всему знакомы с его творчеством

Да можно и не слушать в принципе )

Ну, может быть кого-то это и привлекает, а меня отталкивает. Все же, резкие слова без технического обоснования, в техническом тексте — ну такое. Мне вот тут кинули где-то ссылку на статью с критикой JPA, там тоже ругань — но тем не менее, видно что автор прилично разобрался, смог сформулировать, где это все применимо, а где нет. Вот это нормальный технический подход — хотя автор и говорит, что JPA ненавидит.

И потом, для меня это далеко не первое знакомство, я прекрасно помню выступление на тему ООП прямо тут на Хабре. Когда это вот все повторяется скажем в десятый раз — оно перестает работать обычно.

Конечно он пишет много кода. Даже в этих элементарных примерах такая огромная простыня получается. Причём описанный подход совершенно не гибкий, на реальных примерах все будет гораздо хуже

Я вытираю попу грушами. Это моя попа, он мою попу не видел потому откуда ему знать что это плохо :-)))

Тогда и статью можно описать так: я нелюблю груши, нет ни одной причины для того, чтобы другие их ели.

Я вижу разницу между высказынием своего мнения и критикой чужого с использованием вот таких аргументов.

Бесстыжий плаг

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

я как минимум ожидаю, что будут приведены явные и несомненные плюсы ORM


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

Есть ещё черновик поста с критикой подхода к моделированию данных связным графом объектом.

В качестве альтернативы я предлагаю использовать Агрегаты и Spring Data JDBC.

К текущему моменту, я применил Spring Data R2/JDBC в 4 коммерческих проектах, есть определённы сложности (больше всего не хватает Specification), но в целом доволен и намерен и дальше использовать их в качестве технологии работы с БД по умолчанию.

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

>нанять разработчика, знающего JPA не проблема — берёте с любого рынка и с вероятностью 99% он имеет хоть какой-то опыт работы с JPA;

Пожалуй, когда я имею выбор, т.е. если меня хантят, а не я сам ищу работу, я склонен игнорировать предложения работы, где мне либо предлагают нелюбимые технологии, либо просто не оставляют выбора (очень типично например звать архитектором, при этом озвучивая весь стек до мелочей, включая JPA). Ну т.е. JPA — это такая надоевшая всем рутина, которая при упоминании в описании может где-то оттолкнуть кандидата.

Есть какие-то нюансы, с которыми я не согласился бы — но это тоже нормально.

Ну, ваш пост не портит даже то, что вы декларируете ненависть к хибернейту в самом начале.

Спасибо:)

я склонен игнорировать предложения работы, где мне либо предлагают нелюбимые технологии

Да, согласен с вами, но мне кажется, что разработчиков, которые могут и согласны работать с JPA существенно больше тех, кто могут и согласны работать с Spring Data JDBC/jooq и т.п.

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

Ну и с альтернативами, по моему опыту, новичёк скорее просто не сможет решить задачу и придёт с вопросом.

А в JPA новичёк вполне может с полной уверенностью в себе решить задачу, но попутно разложить гору грабель, которые в тесте будут тихонько лежать, а в проде начнут больно бить, если ревью проскочат.

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

Линус Торвальдс, демонстрирующий фак в сторону Nvidia (известный мем) и Бугаенко, пытающийся сделать какой-то эпатажный вброс против идей, сформированных на базе многолетнего опыта инженерного комьюнити - небо и земля. Егор сам упоминал, что ему перестали приглашать на конфы в Голландии.

>эпатажный вброс против идей, сформированных на базе многолетнего опыта инженерного комьюнити
Вот именно. «ORM зло» — и при этом даже нет попытки описать плюсы и минусы, которые есть у каждого подхода. Тогда как правильный, инженерный подход выглядит так: список плюсов, список минусов, условия, при которых плюсы перевешивают — выводы о рациональности применения. А в таком виде — не более чем пиар.

Невольно вспоминается диалог из "Пиратов Карибского моря" (не применительно к Егору или автору - о их способностях судить не берусь - не доводилось вместе работать):

  • Вы самый жалкий пират, о котором я слышал!

  • Но вы хотя бы слышали обо мне...

Егора перестали приглашать в первую очередь из-за его абсолютно людоедских политических взглядов.

Главная проблема ORM в том, что альтернативы получаются ещё хуже.

>Главная проблема ORM в том, что альтернативы получаются ещё хуже.

Потому что корень зла в РСУБД, а не способах доступа к ним. В адекватном мире у РСУБД рыночная доля уже давно должна быть 5-10%.

Монго с его родным API тоже альтернатива ORM, уже без костылей (хотя и со своими минусами, есно)

Одна web-макака 8 лет назад запретила всем использовать монго (2.6 небось?), потому что её проект был под Neo4j (который тоже рулез), другая вебмакака в 2022 ссылается на неё как на истину в последней инстанции. Эхехе...

Нет, спасибо. Стадию NoSQL все уже прошли лет этак 10 назад.

Кто когда, и по-разному для разных NoSQL.
Например, сетевая (и иерархическая, являющаяся ее подмножеством, но хорошо ложившаяся на архитектуру тогдашней дисковой подсистемы) модель СУБД — это наследие далекого прошлого, когда теоретические работы Кодда ещё не были воплощены в коммерческие программы.
Я-то, по молодости лет, успел поработать только с индексно-последовательными наборами данных (это не совсем СУБД), но были люди, которые работали и с полноценными до-реляционными СУБД.
Ну, а на ПК, уже в 90-х, можно было столкнуться с нереляционной (тоже сетевой) СУБД dbVista (переименованную впоследствии в Raima Database Manager).
Ну, и MS-овский движок ESE (это — конец 90-х, на его базе реализованы AD, Exchange и куча системных служб Windows Server) — он тоже, по имеющейся у меня информации, сетевой (но это неточно — сам я под него не программировал).
Ну, в каких-то областях (графовые, например, или timeseries) NoSQL конечно живее всех живых. Но с другой стороны, вот поработал я как-то с Onetick, на сравнительно малых объемах, и понял простую вещь — что реляционные СУБД при некоторой настройке спокойно переваривают нынче огромные объемы timeseries данных, и настройка эта достаточно тривиальна — потому что паттерны доступа к данным зачастую типовые, данные за сегодня нужны в одном виде, а данные за месяц назад уже практически являются холодными, и нужны очень редко. И простое партиционирование спокойно поднимает производительность реляционной СУБД до производительности специализированной, а инструменты для работы со всем этим остаются такими же — SQL.

Причем в контексте данной темы мисматч между моделью графовой или timeseries БД и кодом (ООП или что там у нас) все равно остается, просто принимает другую форму.

Стадию "NoSQL заменит РСУБД", которая частенько звучала в формате "все переезжаем на MongoDB". Закончилось всё тем, что проблемы РСУБД решили и окзалось, что просто существуют разные типы СУБД для разных задач и наборов данных. Причем, РСУБД - всё ещё самый гибкий и универсальный.

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

Автор претендует на критику ORM, но недоволен почему-то именно Hibernate:

SQL Не Скрыт.

Hibernate-специфичная проблема. В каком-нибудь дотнете все ORM предоставляют к базе тот же интерфейс построения запросов, что даёт LINQ(ближайшим аналогом в Java будет Streams) к объектам.

Трудно протестировать.

Аналогично. EF предоставляет in-memory провайдера, задуманного специально для тестирования — БД мокается List'ами для каждой таблицы.

Java-экосистему я особо не знаю, но, скорее всего, там в 2022 похожие инструменты уже есть и автору стоило бы просто слегка расширить кругозор.

При том, что я люблю EF и много лет использую его в продакшене, конкретно эти претензии вполне справедливы.


Например, SQL иногда действительно просачивается в LINQ-запросы: DbFunctions, EF.Functions. У каждого провайдера БД этот набор функций может быть свой.


По той же причине тестирование на in-memory-провайдере покрывает далеко не все. Перечисленные выше функции не имеют реализации и "работают" только при трансляции в SQL, а при попытке действительно вызвать их на LINQ to Objects вы получите ошибку. И наоборот — относительно легко написать запрос, который выполнится на объектах в памяти, но не сможет быть транслирован в SQL.

SQL иногда действительно просачивается в LINQ-запросы

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

По той же причине тестирование на in-memory-провайдере покрывает далеко не все

Да, но, опять же, если шлёпать очередной CRUD, а не пытаться заставить ORM генерировать конкретный сложный запрос с оптимальным планом выполнения, он покрывает подавляющую часть случаев.

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

У меня, как у шарписта/дотнетчика тоже на моменте с "не скрытым sql" возникли вопросы к автору оригинального текста. Проблемы и недостатки конкретной реализации приписывать всему orm, ну такое

Так в C# то же самое. Автор просто не понял что HQL объектный поверх объектов

Или он не умеет ими пользоваться (¬‿¬ )

Я человек простой, вижу Бугаенко - иду создавать декоратор.

Интересный взгляд, а есть возможность в какой-нибудь экосистеме просто писать в коде типизированный SQL, желательно с учётом диалекта конкретной СУБД?

Вроде ближе всего java'вый JOOQ, у .NET есть EF, хотя там всё-таки довольно толстая прослойка, хоть и качественная.

JPA metamodel, но вам это не понравится.

Root<Student> root = criteriaQuery.from(Student.class);
criteriaQuery.select(root).where(cb.equal(root.get(Student_.gradYear), 2015));

Все типизировано, но невероятно многословно, поэтому им мало кто пользуется.

Не сколько потому что многословно, сколько из-за фигово документированного API, которое создавал надмозг. JOINы там крайне нелогичны, а от вложенных запросов умирают котята.

UFO just landed and posted this here

Это DSL — то есть, мы не пытаемся на языке ООП выражать SQL запросы. :-)

UFO just landed and posted this here

Я имею ввиду, что мы не скованы ограничениями lambda-calculus'а. Мы используем наилучший инструмент под эту конкретную задачу — передачи информации от мозгов программиста к базе данных.

Да, то, что это всё ещё Хаскель имеет свои положительные стороны. Как, впрочем, и отрицательные. :-)

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

И потом, вы вообще уверены, что они (функции) строго типизированы, и их получится обернуть в Java или c#, так чтобы типы параметров и результата можно было предсказать или вывести? Ну даже JOOQ возьмите, вы же догадываетесь, что при каждом вашем рефакторинге нужно по хорошему слазать в СУБД, и посмотреть, а не изменились ли типы данных в таблице? И тут сразу возникает вопрос — а в какой таблице, в нашей базе разработки, или в проме?

В общем, тут есть ряд идеологических проблем, которые с существующими СУБД вряд ли вообще преодолимы до конца.

Если в СУБД в таблицах могут внезапно изменяться типы данных — проблема вовсе не в инструментах.

А что вы предлагаете делать? Я такое в жизни видел десятки раз. Причина совершенно простая — есть интеграция между двумя системами, в одной нужно было что-то поменять, информация до второй просто не дошла — вполне типовая проблема коммуникации. И даже если не внезапно — ну если вас за месяц предупредили, все равно ведь код, котором типы данных СУБД зашиты статически, придется перекомпилировать.

Мы же не можем запретить ALTER TABLE в чужой системе, не так ли? Так что по крайней мере гипотетически SQL имеет вот такую вот типизацию, и нам с ней так или иначе нужно жить.

Вы же понимаете, что если есть программа, которая обращается к чужой БД, и схему этой чужой БД поменяли — то перекомпиляция программы будет наименьшей из проблем? А основной проблемой будет то, что старая версия программы просто перестанет работать, и не важно ORM там использовалась или что-то другое.


По-хорошему, интеграцию между системами надо делать не одной программой "БД-БД", а двумя. Первая экспортирует данные, вторая импортирует, между ними — заранее согласованный формат данных. CSV там, XML или JSON с вполне определённой схемой.

>перекомпиляция программы будет наименьшей из проблем?
Конечно. Еще раз, на всякий случай — я просто констатирую тот факт, что любая ORM как правило имеет дело в объектами, чьи типы данных зафиксированы при компиляции. Соответственно, для нее изменение типа данных в базе — повод перекомпилировать. Это просто такой факт. Изменение типов бывает, и влечет за собой перекомпиляцию, динамически обработать такой в программе скорее всего будет невозможно. Это недостаток ORM? По мне так нет — скорее это фича. Поэтому у меня, в текущем проекте, где требование динамической обработки изменения типов имеет место, ORM и нет.

>Первая экспортирует данные, вторая импортирует
Ну, наверное во многих случаях да, но вот у меня есть система, где суточный поток данных по одной таблице — порядка десятков терабайт. Никакие JSON тут рядом не валялись, единственное что можно, кроме собственно JDBC (ну или другой какой протокол похожего уровня) — это Golden Gate, причем даже его приходится хардкорно тюнить, потому что хрен вы на стандартных настройках такое перекачаете. То есть исключения тоже можно найти, не говоря уже о придумать.

Что-то мне кажется, что при таких потоках данных уже и JDBC становится узким местом из-за привычки всё упаковывать и нагружать сборщик мусора.


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


И да, я бы на вашем месте был сильнее всех прочих комментаторов заинтересован в типизации SQL, потому что остановка потока в пару десятков терабайт в день из-за несовпадения типов звучит очень страшно.

>уже и JDBC становится узким местом
Ну в общем так и есть. Тупо вытащить такой объем целиком — я бы на это бы посмотрел с большим интересом.

>взять что-нибудь бинарное
Kafka Connect активно применяется. Насколько я понимаю, внутри кафки не JSON. И это в чем-то похоже на ваше предложение.

Быстрое гугление показывает, что внутри кафки может быть любой блоб, но конкретно Kafka Connect может работать с форматами Avro, Protobuf и JSON.

Я имел в виду, что внутри кафки в нашем случае не JSON. А так-то он там может быть в частном случае.
UFO just landed and posted this here
>Зачем
Ну тут скорее не «зачем», а «так достаточно типично». Опять же, завтипы — а где они есть, из того что широко применяется?

>в компилтайме проверять корректность вычислений в зависимости от схемы?
Я бы с интересом посмотрел на то, как вы себе это представляете. Но, в тоже время: у нас на сегодня среда разработки и рантайм — это две физически разные сети. То есть, при компиляции мне недоступна реальная схема промышленной БД. И не будет никогда, если реалистично смотреть на вещи.

Я бы конечно хотел бы иметь копию в среде разработки — но при всем моем желании, у меня сотни интеграций (и +50 в год как-то называли темпы роста), поэтому схемы всех этих 150 или там 200 баз при компиляции мне не будут доступны никогда, а те, что доступны, очень сложно поддерживать в актуальном состоянии. Не хочу сказать, что у многих так, ситуации бывают разные, но в том числе очевидно и такие, когда при разработке и компиляции живой базы нет, потому что это «чужая» система.
UFO just landed and posted this here
> принципиально возможные пути решения проблемы
Я таки не очень понимаю, о каких практических путях вообще речь? Ну вот что мне даст хаскель с его системой типов, для определенности, в ситуации, когда на входе у меня некая реляционная база, о которой на момент компиляции я не знаю ничего? Ну т.е. в рантайме мы можем выполнять запросы (но заранее не известно, какие), а при компиляции — просто ничего не знаем, кроме того, что она будет (но это неточно).
UFO just landed and posted this here
Ну например реплицировать. Не, я понимаю что это на самом деле редкая задача, но бывает. И она довольно таки навороченная, если реплика должна вам что-то гарантировать.

На самом деле я просто крайний случай привожу, но по факту же вам никто не гарантирует, что таблица, которая существовала при компиляции, не пропадет (сама, или доступ к ней) в рантайме. Ну т.е. в нашем случае есть репликация из других систем, у нас нечто что называют DWH, или там озеро данных, кому как нравится. И никто нам из других систем в общем случае ничего не гарантирует — и для интеграционных проектов это вполне типовая ситуация.
Поэтому у меня, в текущем проекте, где требование динамической обработки изменения типов имеет место, ORM и нет.

У меня вопрос: что это за проект такой, где схема базы меняется достаточно часто, чтобы типы мешали с этой БД работать?

На самом деле в какой-то степени — любой интеграционный. То есть схема не наша, мы ее не контролируем. Этого достаточно, чтобы априори известным типам нельзя было полностью доверять. Только тем, что мы реально видим в базе.

Но насколько часто схема реально меняется на практике?

Наткнулся на это старое обсуждение...


За год я успел поработать с новым EF Core и хочу добавить, что он поддерживает работу с динамическими моделями, индексируемые свойства в помощь. Правда, для перекачивания терабайта данных я бы его всё равно брать не стал.

Вы же понимаете, что если есть программа, которая обращается к чужой БД, и схему этой чужой БД поменяли — то перекомпиляция программы будет наименьшей из проблем? А основной проблемой будет то, что старая версия программы просто перестанет работать, и не важно ORM там использовалась или что-то другое.

Изначально СУБД как идея задумывалась как средство избежать перекомпиляции (и, тем более — переписывания) программ при изменении конкретных форматов хранения данных. Но изменения схемы БД для этого должны быть, конечно, ограничены чем-то типа Open/Close принципа из SOLID. При бесконтрольных и неограниченных изменениях схемы перекомпиляции (а то и переписывания) избежать, конечно, не удастся.
Остается только убедить разработчиков не менять схему слишком уж радикально.

Смотрите, если схема СУБД изменена таким образом, что программа осталась рабочей — то и перекомпилировать её не нужно. Если стала нерабочей — то одной перекомпиляцией не отделаться, надо программу исправлять.


Это не зависит от того, используется ли ORM или другие способы типизации запросов в программе. То есть необходимость перекомпиляции вообще не является недостатком ORM.

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

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

В рамках того, что предоставляет СУБД XXX версии не ниже YY.ZZ. Пытаться абстрагироваться от конкретной СУБД - прямой путь к тому, что ничего сложнее JOIN-а использовать не получится.

И потом, вы вообще уверены, что они (функции) строго типизированы, и их получится обернуть в Java или c#, так чтобы типы параметров и результата можно было предсказать или вывести?

У JOOQ есть кодогенератор, который берет схему БД и генерирует классы, её описывающие. Таким образом ваш код всегда синхронизирован со схемой БД, и если она поменялась - код просто не скомпилируется, пока не исправите.

Ну даже JOOQ возьмите, вы же догадываетесь, что при каждом вашем рефакторинге нужно по хорошему слазать в СУБД, и посмотреть, а не изменились ли типы данных в таблице? И тут сразу возникает вопрос — а в какой таблице, в нашей базе разработки, или в проме?

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

Так я не говорил, что эти проблемы не решаются. Я просто констатирую, что если мы живем в такой ситуации — то нам ORM не особо годится. Но это не недостаток ORM, это называется ограничение. Ну т.е. например, у меня просто в задании написано, что миграции в БД источника данных мы должны обрабатывать на лету, а на кодогенерацию у нашей команды просто нет ресурсов, потому что у нас сотни интеграций, при этом людей всего меньше десяти. Поэтому мы делаем код на голом JDBC, который динамически строит запросы под текущую схему таблицы, при каждом запуске, а применить ORM в проекте такого типа никто даже и не предлагал. Если же у вас база — это своя база, на которую вы выпускаете релизы синхронно с софтом клиента, то вы вполне можете на такое пойти, и вероятно будет удобно.

>либо кому-то надо отрубить руки, чтобы в прод в обход миграций не лез.
Ну да, это такой организационный способ решить техническую задачу.

Если же у вас база — это своя база, на которую вы выпускаете релизы синхронно с софтом клиента, то вы вполне можете на такое пойти, и вероятно будет удобно.

Ну так 99.9% применений Hibernate именно такие - когда есть какой-то сервис, которому надо где-то хранить свои данные.

И почему-то там вместо "возьмем тринадцатый постгрес" (как у Гитлаба, например) начинается "а вдруг там будет SQLite 0.0.1 beta, нам срочно нужна абстракция над абстракцией! о нет, мы забыли вариант с текстовыми файлами!" непонятно для чего. Потому что если этим всем не заниматься - то Hibernate не особо-то и нужен.

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

>Потому что если этим всем не заниматься
Не, ну из применения ORM можно разный профит получать. Я бы сказал, что в моей практике никогда смена СУБД не была таким фактором выбора. Но это длинная история, и мне скажем неохота такой текст писать. Не стоит овчинка выделки, сто раз уже про все это написаны вполне приличные тексты.
github.com/linq2db/linq2db

Но я не использовал, так что не могу сказать, насколько всё это удобно и работоспособно.

Я использую в рабочих проектах совместно с EF Core. Есть особенности и баги, но в общем и целом - в восторге.

Для .Net в свое время создал SqExpress. Работает с MS SQL, Postgresql и MySql. Заточен в первую очередь на T-SQL, но для остальных диалектов создает полифилы некоторых возможностей (например MERGE).

diesel постулирует эту идею, но на мой взгляд, любой свой DSL намного непонятнее того, что можно в SQL написать. Лучше бы был процедурный макрос, который тоже будет чекаться в compile-time, но запрос будет выглядеть, как запрос (в Rust и такое есть, если что).

Собственно, поддержка SQL когда-то в древние времена с этого — встроенного (embedded) SQL — и начиналась: с раширения языков общего назначения для включения операторов SQL в текст программы.
Но сейчас это, похоже, уже не стильно, не модно и не молодежно.
В Википедии однако есть вот такой список продуктов, поддерживающих встроенный SQL.

Он вроде свой тру ооп язык пилил, интересно выстрелило? кто-то пользуется?

Хм, а если закрыть ORM-объекты как раз теми абстракциями, которые предложены в статье – как будет с производительностью?

То, что предлагает автор, это же по сути active record паттерн как в django?

Только вместо того, чтобы наследовать базовый orm-класс, в котором уже реализована вся crud логика, он фактически предлагает её каждый раз писать вручную. Спасибо большое, всю жизнь мечтал вместо программирования бизнес-логики писать простыни повторяющегося бессмысленного кода

Именно. Он всё время пытается изобрести active record

Да, судя по всему, он это и имеет в виду, не учитывая что orm с active record уже давно существуют, причем работают удобней того, что он предлагает, плюс такой подход нарушает первую буковку в SOLID если уже мы начинаем вести разговор о "правильности"

Кроме геттеров и сеттеров, у объектов нет других методов. Они даже не знают, из какой базы данных они пришли.

Какой эталонный софизм. Если вам в течении жизненного цикла объекта реально нужно знать, из какой базы он пришел, то у вас явно не с ORM проблемы.

Ну а второй софизм - предположение, что ООП это догма и следовательно весь код должен плясать от нее. Нет, тысячи приложений работают с БД в обычном процедурном стиле через jooq или mybatis и горя не знают.

>Если вам в течении жизненного цикла объекта реально нужно знать, из какой базы он пришел
Ну, ради объективности, такой кейс вполне можно вообразить. Репликация какая-нибудь. Правда к этой теме он вряд ли имеет отношение.

Угадал автора по первому предложению. Дальше можно не читать.

Посты и лекции Бугаенко это как авторское кино, когда фильм интересен в первую очередь самому автору и зрителю ничего не должен. Тут примерно также, с точки зрения программирования сферического коня в вакууме это может быть забавно и даже интересно. Но с точки зрения практического применения для решения реальных задача выглядит как какая-то дичь. Кстати, примерно так реагирует неподготовленный зритель в полупустом зале, когда случайно попадает на сеанс авторского кино.

Martin FAwler поправьте плиз. В оригинальной статье правильно через O

Если речь о этом https://martinfowler.com/aboutMe.html человеке, то в английском написании он через O, в русском написании и произношении через А.

Буду обвинять потому что могу. Примерно так автор общается с аудиторией. Доказательства примитивны. Даже назвать-то эти обоснования доказательствами язык не поворачивается. Зато апломб зашкаливает. Вот пример:

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

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

Хотя далее добавляется, что автору не нравится SQL, а потому он должен быть скрыт, но в ORM этого не сделали. Только как автор собрался писать программы без SQL? И чуть ниже видим ответ - SQL всё-таки предлагается использовать, но только перенеся его в другое место. Но кто-то же должен написать SQL в этом другом месте, ведь так? И почему автор в этом другом месте не видит нарушения всех столь важных принципов, за которые он так громко негодует по ходу статьи?

Второй недостаток, который всё же привёл автор - сложность тестирования. Но тестирование чего конкретно? Вот что говорит автор:

Когда какой-либо объект работает со списком записей, ему необходимо иметь дело с экземпляром SessionFactory. Как мы можем замокать эту зависимость?

Оказывается ему хочется "замокать" SessionFactory.Но зачем? Что бы тестировать Hibernate? Ну тогда уж и всю JVM стоит подробно и со всей дотошностью обложить тестами. А вдруг там ошибка? И мы, ради программы Hello world, напишем эдак сотню мегабайт тестов. А что, ведь действительно можем ошибку найти!

В целом очень жаль, что подобные статьи вообще появляются на данном ресурсе. Самоуверенное бесстыдство, наряду с отсутствием здравого смысла, не просто не являются чем-то полезным, но пропагандируют оболванивающие молодёжь "знания", что гораздо хуже самолюбования автора с его зашкаливающим апломбом.

Вы не поняли, sql тут спрятан в модели, а не расползается по прикладному коду. Ну и под замокать тут понимается замена объекта моком, а не обкладывание его самого моками.

Вы не поняли, sql тут спрятан в модели, а не расползается по прикладному коду

Кто пишет модель? Если тот же самый программист, то это как раз расползание его внимания по разным частям программы в то время, когда он должен воспринимать бизнес-логику как что-то целостное. И даже если другой, то всё равно имеем существенно разные зоны ответственности, отгороженные одна от другой исключительно благодаря религиозным принципам.

под замокать тут понимается замена объекта моком, а не обкладывание его самого моками.

Пишем интерфейс вроде getRecords и имеем полное счастье. Но автор же задаёт вопрос - как? Он не знает, что такое интерфейсы? Или?

Вообще, тестировать каждый чих - это неправильно. Тестировать возвращаемый SQL набор записей на полноту и другие проблемы - это очень трудоёмко, вполне сопоставимо с тестированием JVM. Так писать программы может себе позволить лишь тот, у кого есть в наличии бесконечное время. Но в реальности ни у кого такой опции нет. Отсюда очевидный (и не самый лучший для автора) вывод.

Не важно, кто её пишет. Пишется она один раз, а используется в сотни местах. Это называется абстракция. Ну а в крупных проектах да, пишется она отдельными людьми.

Чем проще абстракция, тем проще её мокать. И абстракция "дай пользователей по таким-то условиям" куда проще, чем "вот тебе SQL запрос, распарси его, правильно обработай и сформируй соответствующий респонс" и надёжней, чем "в ответ на такую строку запроса выдай такие-то объекты".

Совершенно верно. Поэтому можно поверх что SQL, что HQL построить репозиорий с какими-нибудь ограниченными запросами. Но дело в том, что SQL тогда останется не протестированным, а его бы тоже хотелось.

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

SQL можно тестировать интеграционными тестами. Вот прямо, создать заранее известные тесту наборы записей, проверить, потом по желанию всё это удалить через rollback, чтобы остальным тестам не мешать.
Главный недостаток — время подтверждения от тестов. Но тут уж решать нужно, что важнее — протестированный запрос или прошедшие за 0,1с тесты.

Не важно, кто её пишет

Это очень важно с точки зрения развития системы.

Это называется абстракция

Автор не про "абстракции вообще" говорил, он говорил, что ему не нравится абстракция ORM. Но предложил он в итоге простейшую прослойку, в которой сам использует какую-то ORM. То есть вместо сокращения числа абстракций автор плодит сущнсоти без какой-либо необходимости.

Чем проще абстракция, тем проще её мокать

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

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

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

Советую автору попробовать для строготипизированных языков например драйверы Mongodb : чистые модели , автосессии , удобные типизированные запросы и плюсом поддержка Expressions например как LINQ в C#

Мне кажется, или в прилагаемых примерах он Spring Data JPA в итоге изобрёл?

С ОРМ можно жить, другое дело что он дубовый, как хороший гроб.
Предлагаемый подход, впрочем, еще хуже.
Живите, чего уж.

Посыл, что ОРМ зло - верный. А вот решение - не сильно лучше.

Зачем вам ООП для запросов в БД? Чтобы решать несуществующие проблемы декораторами?

На мой взгляд - слишком фанатичное отношение к принципам ООП. Как говорил дядюшка Боб, где лучше применять ООП подходы, где то процедурные. В случае orm, как раз удобнее использовать процедурный подход, как в работе с rast api. Нужно использовать то, что понятно и удобно, а не то, что кажется вам правоверным

Ну не то чтобы прям ORM плохой паттерн. А плохо масштабируемый. Он полачалу экономит строки кода, а потом может сильно напрягать. Но все зависит от случая, я бы не сверхобобщал. А что этот инструмент не идеален - это уж извините. В мире еще очень много разочарований :)

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

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

совсем не должен

дата-маппер придумали не для аналогии SQL, а для написания чистых сущностей, просто потом заммапить стейт... тот факт, что из БИЗНЕС-сущностей делают отображение БД — проблема

в PHP есть аналоги hibernate — Doctrine ORM, и один из авторов явно пишет о том, что не стоит в репах делать запросы-чтения

https://ocramius.github.io/doctrine-best-practices/#/89

не он один об этом пишет, но как референс привел

до кучи проблем добавляют архиекторы, спускающие разработчикам не домены, а схемы БД готовые

до кучи проблем добавляют архиекторы, спускающие разработчикам не домены, а схемы БД готовые

проблема в том, что если архитектор не подумает о схеме бд, то потом будет много проблем, так как большинство современных разработчиков полностью не осознаёт какое разнообразие проблем можно получить с бд .

С другой стороны непонятно, как схема бд мешает разработчикам писать доменную модель?

Понимают-не понимают, но составление схемы БД — задача не архитектора, а специалиста по базам данных.

Ну как правило, архитектором не рождаются, а становится инженер с огромным опытом реализаций, у которого были сотни схем БД и сформировался приличный опыт реализации различных типовых решений.

Конечно я могу нарисовать ERD, но мне и переложить это на схему БД не сложно. А если я буду словами писать"мне нужна схема бд для пользователей, ролей, клаймов" то велика вероятность, что DBD мне придумает такое, что потом все разрабы будут страдать.

Более того архитектура решения очень часто диктует как именно нужно хранить данные. Отличный пример - различные CAD системы с множеством расширяемых типов объектов и многоуровневым наследованием. Можно реализовать классическим TPH и иметь кучу джойнов. Можно сделать TPT и иметь проблемы с поиском объектов весом больше одной тонны по сотням таблиц. А можно реализовать а-ля колоночную БД. И каждый из этих вариантов имеет право на существование, а что именно делать решает архитектор, а не DBD.

Я правильно понял, автор заявляет, что ORM зло, потому что там что-то не так с ответственностью (якобы) и объект "отвязан". Потом изобретает ActiveRecord, что в свою очередь тоже ORM. И объект там все так же "отвязан". Но зато велосипед свой! Подскажите, это какие-то наркотики или что? Как такое сожно на серьезных щщах делать? И кто за это плюсы ставит? +15 на данный момент у статьи, это как?

Меня тоже удивляет, что Егор пользуется популярностью. По-моему, типичный Кулибин, что в программировании, что в управлении

Самое главное (я конечно не суперспец по джаве) что предложенный код выглядит просто плохим.

Мне лично кажестя, что это товарищ просто не видил комерческих проектов с сотнями таблиц. Я бы его отправил бы на пару лет писать вот все эти обертки руками без кодгена, чтобы он понял зачем вообще придумали ОРМ и почему в 95% случаев производительность запросов в ОРМ практически не важна.

А потом он сможет вылезти из мира очень плохих библиотек в джава в мир дотнета и офигеть, что мало того, что EF пишется лучше и работает хорошо, но что там можно оказывается и самим запросы писать! А еще есть миграции!

Это он еще про репозитории не знает :)

Не факт, что он не знает, может быть, для него его идея важнее, чем все репозитории мира вместе взятые. Если послушать его доклад, то сначала идет мотивировочная часть (типа, ООП, чтобы было проще разобраться) потом идет основная часть (например, пишите вместо статических функций, объектные ориентированные обертки, которые эквивалентны лениво вычисляемым статическим функциям семантически, но без выигрышей от ленивости) которая противоречит "проще разобраться". А когда ему указывают на противеоречие, он вместо аргументов использует эмоции или вообще ответ сводится к "я так вижу"

Это "мир очень плохих библиотек в джава" по сути еще и лишь по вине автора, который выбрал голый Hibernate и по ходу статьи пытался изобрести кривой аналог Spring Data JPA

Собственно, если посмотреть на Spring Data (которым пользуется большинство в джава-мире), то сразу видно, что там все гораздо стройнее и красивее. И пишется лучше, и самим запросы писать можно, и абстракции на любой вкус, и транзакции декларативные, и генерация запросов по имени метода, и еще куча фич. Вообще красота, буквально 1 строчкой любой запрос делается, 0 бойлерплейта

Лично я все равно предпочитаю Spring Data JDBC или JOOQ (и не люблю ORM в целом), но просто глаза режет от того, что каждый второй пишет про EF, приводя в плюсы то, что есть и в самом используемой библиотеке джавы для бд, просто автор этого, видимо, не знал, или не хотел показывать)

Лично я не пользовался Spring Data, судя по документашке это гораздо менее лаконично чем EF + Linq. Весь плюс дотнета в данном случае состоит именно в связке с Linq.

Зато, в свое время я пользовался Хибернейтом и НХибернейтом, и это мрак в 2022 году

Хибернейт без спринга - да, мрак.

Spring Data - это буквально 1 строчка в репозитории, объявляющая метод. Реализацию за вас сгенерирует сама спринг дата. И получается, что у нас репозиторий является интерфейсом, который просто содержит объявления нужных нам методов с парой анноташек (реально 0-2 на метод). А дальше всё, только дергаем эти методы. Реализовывать, писать какие-то бины для настройки и творить прочий бойлерплейт не надо. Пагинация, круд и прочее из коробки

Сама генерация реализации происходит одним из трех способов. Или по имени метода (типа findByIdAndNameOrderByName, он сам парсит имя и понимает, что хотим), или по jpql/hql в анноташке Query, или по голому sql в той же самой анноташке

Транзакции делаются одной аннотацией, остальные фичи бд не сильно сложнее. Глубина выборки регулируется EntityGraph и тд. Много всего

Лично я как-то пытался в шарп вкатиться, даже дважды. Как-никак один из мажорных языков, по сути единственный неощупанный у меня. Но каждый раз я натыкался на локальные приколы шарпа (падение перфоманса в 20 раз из-за перестановки 3 множителей в выражении из-за кривого инлайна, любовь разрабов пихать кучу логики в проперти, из-за чего приходится гадать, безопасно ли обращаться в цикле к ним, и тд) и его экосистемы (тот же десктоп - просто мрак и ужас. Куча технологий, и все или заброшены, или в превью, и весь этот монстр Франкенштейна куда-то движется. Winforms, Xamarin, WPF, WinUI, UWP, MAUI... столько всякой хрени, а использовать нечего. Редакторы нормально поддерживают только Winforms и WPF, которые давно заброшены и устарели. WinUI и UWP только появились и сразу сдохли, MAUI вообще до сих пор в превью, но должен заменить в очередной раз все остальное....)

В общем, не сложилось у меня с шарпом и его чехардой)

Для себя выбрал Джаву (на которой и работаю), Котлин (который просто очень вкусный для кроссплатформерной клиентской разработки), Тайпскрипт (веб фронтенд, но в перспективе, когда допилят, мб получится заменить Котлином) и Раст (недавно ознакомился, просто влюбился в язык. Оставил его вместо плюсов для системщины, хочу заодно попробовать сделать бэк на нем, чтобы сравнить в этом с Джавой)

Жду когда автор этого чудо-текста познакомится с linq и ef.

Предложенный в статье подход - хороший пример нарушения принципа единой ответственности. При этом затронутая в нем проблема (rish domain model vs simple domain model) вообще не раскрыта, и вместо этого мы видим обычную вкусовщину.

Сам по себе язык SQL крут. И всем стоит его изучить. Он не такой и сложный в целом. Это реально язык манипуляции с данными. Пока вы там будете перебирать коллекции в цикле и что-то с чем-то матчить руками, sql сделает это за вас одним запросом;)

Да, есть нюансы, такие как - сложные запросы надо уметь профилировать, чтобы не нарваться на зависание запроса, блокировки итд. Приходит с опытом.

Вообще можнл писать и на чистом sql п приложении, но существенный недостаток - это нет проверки правильности sql при компиляции. То есть, можно ошибиться в выбираемом поле и получить ошибку в рантайме. Особенно это актуально, если надо рефпктрить часть с data слоем с sql.

Выносить куски логики приложения в хранимые процедуры? Не, спасибо.

Так я ни слова не сказал про хранимые процедуры, за меня не надо додумывать.

Можно в приложении писать raw sql и ручками мапить в объекты, я про это говорил.

Можно и на ассемблере все писать, только зачем? ORM экономит тонны времени, если им правильно пользоваться. Ну а маппинг руками это то ещё удовольствие частенько

Так а причем тут ассемблер? Вы наверное Джун, воспитанный на курсах и паттернах;)

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

Если в приложении надо выбрать, отфильтровать, соединить, объединить какой-то массив данных, то вполне можно написать компактный sql запрос. А потом смапмить ответ в структуру.

Я об этом и писал

Начал читать, тема интересная и актуальная.

Но после пары абзацев поймал де-жавю... заголовк SQL-speaking objects, да ладно, неужели Бугаенко опять? И правда оказался он :)

Попробуйте ef core подобные орм и у вас вообще проблем не будет.

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

Почему вы считаете Active Record анти паттерном?

Sign up to leave a comment.

Articles