Как стать автором
Обновить

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

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

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

С другой стороны, ORM умеет генерить селекты c where и некоторые умеют в left join. У EF правда есть linq to entities, где можно совращать гусей, но тут синтаксис максимально близок к sql. Если честно, совсем не понимаю, что в этих запросах может быть сложного.

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

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

Категоричность умиляет. Обычно по ней можно делать вывод, где человек на графике нормального распределения. В айти сейчас столько людей, которым не надо было в айти, что с этим фактом надо считаться при выборе технологий. И именно поэтому ORM - крайне опасен. Всякого навидался в реальных проектах. Из некоторых выпиливали nHibernate под корень, так как перестал иметь смысл, поддержку и стал rocket science'ом для молодёжи со всеми вытекающими. А с EF городили такого, что пришлось просто запретить его в проектах сложнее домашней странички.

И что пишите сырой SQL? Как раз у вас явно заметно "я не умею пользоваться/управлять разработкой значит это плохо".

Дай * вилку он себе глаз выколит.

Нужно всего-то включить печать запросов в локлальном и дев окружении

И всего-то научить любого джуна понимать, что там написано. Джунам намного полезней пописать SQL руками, чем создавать им ложное чуство уверенности, что они что-то контролируют в генераторе.

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

Explain прикладывать надо, а не SQL :)

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

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

  • ничего

  • просто SQL-запрос

  • Explain с локалхоста вообще без данных

совсем не понимаю, что в этих запросах может быть сложного

Вот эта ремарка вызывает ощущение, будто автор очень мало и очень поверхностно поработал как с ORM, так и с SQL. Хотя сама статья, если честно, вызвала прямо противоположные чувства.

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

Самое первое, что приходит мне на ум, когда я слышу о том, что писать запросы (на чистом SQL или через ORM - не важно) это просто - это попросить автора этих слов разобрать порядок выполнение какого-нибудь более-менее сложного запроса. Хотя бы в пределах одной СУБД. В каком порядке будет выполняться запрос? Какие индексы будут задействоваться? А будут ли они задействоваться? А как сделать так, чтобы они задействовались? А что произойдёт, если данных будет очень много? А можно ли переписать запрос более эффективно?

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

В каком порядке будет выполняться запрос?

Язык SQL декларативный. В том порядке, в котором захочет СУБД. Проще её саму спросить. Индексы БД не обязана использовать, но обычно использует. Если данных много - крутите кэши под ваш сценарий. Может у вас хэви инсерт, а для селекта можно подождать - тогда кэши не спасут.

Сомневайтесь, это полезно. Проясню ремарку: сегодняшние orm, пока в них не внедрили нейросети, очень сильно ограничены по функционалу. В сгенерированных запросах нет ничего сложного. Нет латерал джойнов, в юнионы по-моему ни одна не умеет. Даже group by с having умеют единицы. Про CTE вообще молчу. Человек намного изощрённее любой ORM. Естественная нейросеть способна нагородить куда более ужасную и изощрённую конструкцию, чем самый продвинутый фреймворк

Нет латерал джойнов, в юнионы по-моему ни одна не умеет. Даже group by с having умеют единицы. Про CTE вообще молчу

Это ерунда полная, sqlalchemy в питоне умеет все это и даже больше, а все, что умеет hibernate джавовский даже представить сложно.

Опытный разработчик строит запрос под схему данных и индексы, заранее разделяя запрос на подзапросы, что бы он был эффективен. И вот ваш второй абзац и поясняет, почему ORM не silver bullet и применим только в определённых условиях.

Надеюсь, фраза про нейросети в orm была шуткой))

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

Простите, а когда это "потом" начнётся? Один проект у меня уже 13 лет на ORM, пока всё хорошо. Или имеется в виду, что падает скорость разработки? Тогда тем более непонятно. ORM как раз нужен, чтобы снизив сложность, её повысить. И это работает.

Имеется в виду что использующие ОРМ часто мало понимают как будут храниться данные. Если данные сложносвязанные, оптимально это сделать сложнее или вообще невозможно чем не используя ОРМ. Вообще не трогаю случаи когда куча логики вообще внутри БД в виде триггеров или хранимок. А на начальном этапе, возможно скорость разработки будет выше, для тех кто с БД не очень дружит. А если дружит то ОРМ только усложняет все.

Имеется в виду что использующие ОРМ часто мало понимают как будут храниться данные.

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

Если данные сложносвязанные

То возможно база спроектирована неправильно.

Вообще не трогаю случаи когда куча логики вообще внутри БД в виде триггеров или хранимок.

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

И да, я пишу много логики в БД. И нет не хватит. Почему? Потому что есть как бы такое правило, что чем ближе к данным находится логика, тем она быстрее. Хранимка получает данные условно напрямую, избегая задержек передачи по сети. Триггером можно заботится о консистетности и так далее. База - давно не тупое хранилище.

В теории всё так. But.

In theory, theory and practice are the same. In practice, they are not.

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

p.s. триггеры, юники, ключи и прочие констрейнты работают не только для встроенного языка

uniq на шарпе вы 100% не построите на более менее большой бд

Я тоже противник орм, мне достаточен SQL generator, например knex(nodejs)

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

ORM не может быть злом. Это отражение плоских данных в структуру объектов ЯП. Либо это делать руками, что по сути ни что иное, как обезьяний труд. Либо всё таки положиться на компилятор/ЯП. Проблемы начинаются в отказе от SQL для написания запросов. Простые запросы и правда нет смысла фигачить руками, а что по-сложнее уже нет смысла генерировать, так как SQL самодостаточен.

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

Почему-то минусуют. А ведь есть такие подходы к архитектуре, как Data-driven design, stream processing и data pipelining.

Если мы говорим про бизнес-логику, то это сложные объекты со сложными сценариями бизнес-консистетности данных. Речь не про простейшие констрейты, которые мы можем обеспечить в БД, а гораздо более сложные. Соответственно, нужно работать с объектами. БД в данном случае выступает хранилищем данных, из которых эти объекты состоят. Единственная альтернатива ORM, это доставать плоские данные и вручную распихивать их в объекты, что не что иное, как "ручной ORM", а зачем в наш век делать что-то вручную, если это довольно легко автоматизируется? Я нахожу только два ответа: старая привычка или религия.

В моей практике я не сталкивался с ситуацией, когда ORM мне мешала, всегда только помогала. Всегда. Это вовсе не отменяет требования знать SQL, как устроены СУБД, индексы, транзакции и прочее прочее.

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

ORM makes simple things easier, but hard things impossible.

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

О, да-да, натюрлих! Пока запросы типа "найти запись по id" или пока глубина джойнов не больше 2-х, то ORM худо бедно попрет. Но в проектах, где БД содержит 1000+ криво спроектированных таблиц да еще без FK или с такими FK, что хрен расплетешь - что на что ссылается (я говорю об унаследованных системах с сотнями миллиардов и больше записей), то медленно (а, может, и не медленно) приходит трындец. Вот всю бизнес-логику фигачить на pl/sql - этого не стоит делать точно. Лучше БД оставить хранение данных, а логику выносить в приложение.

криво спроектированных таблиц ... с такими FK, что хрен расплетешь

Как вариант, грамотно спроектировать к ним 1000+ view для использования с ORM.

Ну, как вариант да. Но напомнило анекдот:

"Объявление в борделе.

Секс - 100 баксов

Наблюдение за сексом - 200 баксов

Наблюдение за наблюдающим за сексом - 500 баксов"

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

Тут так: если хотите читаемый и поддерживаемый код: пишете на орм. Хотите любить гусей - пишете на чистом sql. Как-то одно другому мешает? На практике оказывается, что сложные запросы нужны, мягко скажем, не в критичном количестве. А ежедневная рутина делается проще на орм.

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

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

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

И кто вам запретит писать сложные тяжёлые запросы? У вас кто-то насильно отбирает SQL?

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

Почитайте про развитие архитектуры сайта hh ru, где люди перешли с точностью до наоборот от самописных sql на orm по примерно тем же причинам. И не надо только про производительность, у них миллионы запросов в секунду.

Обычно хайлоад не насилует базу напрямую.

Ну вот такой жизненный цикл разработки многие проекты и проходят.

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

Логика без ORM ещё не означает логику в базе.

Может и не означает, но вы сказали "многие проекты переносят логику на сторону базы".

sql портянки с вложенными запросами и тоннами джоинов?

Возможно, это признак плохо спроектированной БД

А если нельзя перепроектировать БД, то можно добавить вьюшки в БД

А, тут и без меня уже всё написали.

И да, пока всё просто не усложняйте

Keep it simple, stupid

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

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

примеры бы очень не помешали, насколько проще станет работать

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

Я уже однажды приводил пример:

Orders.Where(o => o.IsCompleted && o.CreatedDate.Year > 2020).OrderBy(o.ID)

Трансформируется в:

SELECT * FROM db.Orders a WHERE a.IsCompleted = 1 AND YEAR(a.CreatedDate) > 2020 ORDER BY a.ID

Разница невелика на первый взгляд. Просто чистый sql ещё предстоит отправить через коннекшен, получить результат и распихать по полям объектов. ORM сам всё делает, строка самодостаточна.

$query = Product::find()->alias('p');
$query->joinWith('category c');

if ($filter->managerEmail) {
  $query->joinWith('manager m');
  $query->andWhere(['m.email' => $filter->managerEmail]);
}

$subQuery = ProductImage::find()->where('product_id = p.id');
if ($filter->hasImages === true) {
  $query->andWhere(['exists', $subQuery]);
} elseif ($filter->hasImages === false) {
  $query->andWhere(['not exists', $subQuery]);
}

return new ActiveDataProvider([
  'query' => $query,
  'sort' => ['attributes' =>
    ['p.created_at', 'p.name', 'c.name', 'm.email']
  ],
]);

Параметр sort задает разрешенные поля для сортировки, конкретная сортировка задается в URL, ActiveDataProvider автоматически применяет сортировку к запросу. С SQL этот код будет выглядеть гораздо длиннее и запутаннее.

В целом маппинг на сущности нужен не столько для построения SQL-запросов, сколько для задания типизации в коде, например в аргументах функций. И соответственно для использования возможностей IDE - подсказки возможных свойств, поиск мест использования класса или свойства, и т.д.

=== true

Elseif === false

Этапять🙄

Что не так? Автор этого кода явно прописал, что происходит только обработка значений true и false. Если там, например, null или, не дай бог, пустой массив (мне доводилось видеть всякое), ничего не произойдёт.

советую прочитать про валидацию)

А я совета не просил) Я попросил объяснить, что не так в коде автора?

все же очевидно - явная проверка на true/false выглядит как маразм

Очевидно кому?

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

В данном случае очевидно, что допустимыми значениями фильтра являются true, false и отсутствие значения. Это типичная ситуация для фильтров - возможность отключения фильтра.

Вот если бы вы не ёрничали, а попытались сформулировать словами, то вы бы догадались, что это обработка фильтров, а значит hasImages будет иметь 3 значения - true, false и null, что означает "только без изображений", "только с изображениями", "без фильтра по изображениям".

почему бы не проверить на null сначала, а потом уже работать только с одним типом?

честно скажу - очень режет глаза явное сравнение с булевыми значениями

Зачем мне лишняя вложенность if? Тем более, что для остальных фильтров проверки на null нет. Тут важнее универсальность. В реальном приложении можно enum сделать, но для примера мне было лень.

очень режет глаза явное сравнение с булевыми значениями

Может вы еще и на null проверяете оператором !, а не явным сравнением?)

Проект у меня разрастается, но пока нигде не чешится внедрять ORM. Спокойно на стороне клиента готовлю JSON, затем отправляю на сервер, далее проверяю JSON , либо возвращаю, либо сортирую его на запросы и в базу. Я не против ORM, я даже за, но зачем тратить ресурсы на ORM, когда их можно потратить на JSON.stringify, JSON.parse и на другие Нода-замедлители? В моей понимании мира всё движится от простого к сложному. Конечно, я тоже когда-нибудь начну применять круто сваренную абстракцию, но это будет не скоро.

Значит у вас проект пока еще слишком простой, и похоже вы пишете его один.

но зачем тратить ресурсы на ORM

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

Я вот раньше думал

думать - это вредно

В заголовке заявлено "Пора внедрять ORM в вашу систему", причем это заявлено в разделе программирование.

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

Первое, что приходит в голову при разработке системы: как хранить данные. "База данных" ответ. Не, ну серьёзно, а что ещё? Какая? Ну просто база данных, любая. Чтоб данные хранила. Ну не в экселе же хранить, серьёзно. Берём {vendorname}sql.

Вот я разрабатываю систему для микроконтроллера, с 16 кб памяти, какая нафиг база данных?

Вот я разрабатываю приложение, которое из CSV файла строит графики и пихает их в картинку, какая тут нафиг база данных нужна?

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

Простите, а зачем вам база данных в контроллере с 16кб памяти?

мне надо хранить данные. Чтобы хранить данные не всегда нужна база данных

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

Есть одна неустранимая проблема. Она в характере табличных данных и представлении этих данных в виде объектов.

ОРМ берет табличные данные, тащит их в память, представляет их в виде объектов и дает работать там с этими объектами процедурным образом. Это эффективно для работы с небольшим количеством данных, причем в древовидном виде (объекты/поля, ссылки на другие объекты).

В реляционной базе данных используются данные в виде таблиц и специально сконструированный декларативный язык для доступа к этим данным. Это: 1) эффективно для больших объемов табличных данных. 2) Позволяет встроенными средствами поддерживать целостность и непротиворечивость данных.

Таким образом вы или работаете с запросами БД или тащите данные в память и там с ними работаете. Причем если вы хотите большие объемы и целостность, вам придется реализовывать самому часть функциональности реляционной ДБ.

Поэтому ОРМ не является серебряной пулей. Более того, в неопытных руках это достаточно опасная штука.

По моему где-то так.

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

На практике эта скорость никому не нужна, пока приложение не умеет делать то, что оно должно делать. Быстро, но фигню - это не надо.

На практике получается парадоксальная ситуация: переписанная логика pl/sql в сишарп начинает работать быстрее в разы. Как такое возможно? Теоретически никак. Доступ к данным у шарпа по определению медленнее.

На практике эта скорость никому не нужна

Значит ваш проект просто не вырос до тех объёмов, когда все эти неэффективные запросы ставят стек на колени и всё судорожно переписывается не то что на "без ORM", а вообще в сторону NoSQL.

Есть одна неустранимая проблема. Она в характере табличных данных и представлении этих данных в виде объектов.

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

ОРМ берет табличные данные, тащит их в память, представляет их в виде объектов
В реляционной базе данных используются данные в виде таблиц

Аналог таблицы в оперативной памяти приложения это массив/коллекция, а не объект сущности.
Аналог объекта в базе данных это строка/кортеж, а не таблица.

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

Где-то в оттенках серого между SQL и ORM в Java-мире есть MyBatis, который позволяет конструировать контролируемый и разумный SQL код, маппить результаты запросов на сущности. Тюнинг запросов и проблемы с перфомансом в нём решаются довольно дёшево. А сами запросы могут быть настолько сложными, насколько это нужно. Простотой, понятный и удобный. Со всеми преимуществами чистого SQL и без недостатков ORM.

маппить результаты запросов на сущности

Это ORM. По определению.

без недостатков ORM

Заманчиво. Орм без недостатков орм. Как это?

Собственно, меня огорчает, что люди противопоставляют ORM чистому SQL. Одно другому не мешает. ОРМ - маппинг. SQL - язык запросов.

MyBatis не является ORM. Его сущности не связаны с таблицами. Разработчик сам решает откуда и как брать данные, как таблицы или view должны быть сджоинены, как агрегировать данные для получения результата. Это позволяет для каждого конкретного случая написать максимально эффективный SQL.

Дружище, определись уже. Либо оно мапит данные на класс, то тогда оно ORM, либо не маппит, тогда оно не ORM.

Способность маппить данные на класс не делает что-то ORM-ом. Object Mapping и Object-Relational Mapping - это не одно и то же.

Да ладно. А MyBatis какие данные маппит? Случайно не реляционные? Может оно XML парсит?

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

В классическом ORM есть набор классов/моделей и их связей, которые соответствуют таблицам и их отношениям в базе. ORM генерирует SQL запрос к базе на основании этих моделей, и маппит данные результата на эти модели. Это ORM. Такой, как Hibernate или Django ORM.

MyBatis же собирает SQL запрос из тех SQL-кусочков, которые дал ему разработчик. И маппит данные результата на классы, которые указал разработчик. Это просто маппинг. Классы в этом случае могут вообще не отражать схему таблиц. Тут нет никакого ORM.

Если речь про статью MyBatis «на минималках», то там ни слова про ORM, кроме тега "orm, orm всегда медленный".

На самом деле всё зависит от сложности проекта, схемы и размера базы.

Если проект простой, а данных мало - почему бы и не ORM.

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

Ну в C# есть Dapper, он тоже не ORM, хотя данные тоже маппит результат запроса на объект. Как указали выше, тут нет "Relational" данные маппятся на плоский объект

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

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

------------

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

А потом, написав тонну ORM-ориентированного когда на условном SQLAlchemy / SQLModel и прибив его гвоздями к своей бизнес-логике, вы решите обновить их до новой мажорной версии дабы идти в ногу со временем. Если вам действительно некуда больше инвестировать свой временной ресурс, то ради бога.

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

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

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

Вообще, неприятно удивляет, насколько обесценивание SQL проникло и в среду разработки и с среду data science одновременно.

Казалось бы, вот он, язык нового времени, когда у нас многоядерные процессоры стоят даже в тостере и в облаке есть всякие автоскейлинги. Вот он, способ отскалировать код на 1000 нод, просто выполнив его в другом коннекшене. Способ сократить объем данных, передаваемых между машинами, в сотни раз. Но не, не надо. Зачем оно прогерам, когда есть ORM, зачем оно DSам, когда есть Pandas, зачем оно аналитикам, когда можно колонки и столбцы драгдропом потаскать?

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

Была розовая мечта через ORM не зависеть от конкретной базы данных, но на практике оказалось, что оно никому не надо.

Мне надо

Оо, воспоминание разблокировано - в далеком 2013 году кодил я проект для банков. Был у меня nhibernate, в нем пара кастомных типов для блоб полей / json, и несколько специфичных хранимок / запросов типа иерархических на нативном sql. Все банки-клиенты сидели на оракле.

И вот однажды приходит босс и говорит - у нас клиент появился, на MS SQL. Сколько надо времени / денег, чтоб сделать поддержку ms sql?

Я взял день на подумать и поэкспериментировать, а в итоге через 3 часа все сделал. Сделал реализации кастомных типов, переписал хранимки / нативный sql. Немного подшаманил гибернейт, чтоб по строке подключения определяло тип СУБД. И все взлетело.

ORM придумали для тех, кто не хочет тратить время на их поддержку.

А где тег "вредные советы", "сарказм"?

Использование ORM и по смыслу (напиши мне SQL-запрос на основе команды на другом языке), и по качеству результата (ниже плинтуса) эквивалентно использованию ChatGPT для генерации SQL-запросов

Я вот тут привел пример кода, покажите пожалуйста ваш код с SQL с аналогичной функциональностью, который по смыслу и по качеству результата будет лучше.

Тут не один запрос, а простейшая параметрическая кодогенерация. Это всегда решается на уровне приложения или процедурного языка (PL/SQL, T-SQL, PL/pgSQL). ORM для этого не обязателен, на любом языке программирования из коробки можно собрать такой же запрос, в т.ч. с защитой от SQL Injection

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

С телефона я сейчас печатать код не буду, 1 мая, увы, не за рабочим местом)

Но тут такой примитив, что даже ORM не должен налажать, наплодив n + 1 проблему, избыточные джойны и отбитые списки в where. Хотя я не уверен) Да и не факт, что вы сами знаете как этот запрос сейчас у вас фактически выполняется)

Джойны и все остальное в этом коде задаются явно, ORM ничего не добавляет. Запрос соответствует тому, что написано в коде.
С SQL у вас как раз будет избыточный джойн с таблицей manager, потому что вы не захотите делать отдельную конкатенацию для JOIN внутри if, куча конкатенации для WHERE и построения финального запроса, и куча копипасты для обработки сортировки, потому что со строками сырого SQL переиспользовать этот код для разных сущностей нельзя, либо придется парсить SQL и делать свой query builder, а там уже и до ORM недалеко.

Господи, ну вы что, не представляете генерацию параметризованной SQL-лапши без ORM? Это ж просто строка, её можно вертеть как угодно через условия в переменных и if / циклы и т. д. Если не требуется джойнить managers - да просто не генерируй соответствующий кусок строки, и всё))

Нужно переиспользовать шаблон логики? Не беда, человечество давно придумало шаблонизаторы (Jinja, Twig и т.п.) и пользовательские функции на языках, встроенных в БД (что даёт не только более чистый и стабильный код и на стороне приложения, и на стороне базы, но ещё и более безопасный код - см. например security definer-функции в PostgreSQL).

Единственное, что действительно необходимо для работы с БД из приложения - драйвер для подключения к этой БД (примеры для Python - psycopg / asyncpg, cx-oracle, pyodbc и т.п.)

ну вы что, не представляете генерацию параметризованной SQL-лапши

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

Не беда, человечество давно придумало шаблонизаторы

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

но ещё и более безопасный код - см. например security definer-функции в PostgreSQL

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

Чтобы всем было наглядно видно, что эту мешанину будет сложно поддерживать

Я принципиально не понимаю как кривую и функционально неполную абстракцию над кодом может быть проще поддерживать, чем чистый, максимально explicit код, тем более такой простой, как SQL)

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

Ну это ожидаемо)
К счастью, современные ОРМ уже залатали 99% дыр в безопасности, которыми они посвистывали ещё лет 5-7 назад. А сливы данных из-за такого отношения к инфобезу уже к ORM не относятся
Вот если б ORM делали бы вид, что ещё и ролевые модели в БД умеют генерировать, вот тогда б точно приехали))

P.S. завтра утром до ноута доберусь, может накидаю какие-то варианты, раз так сильно просите)

Я принципиально не понимаю

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

уже залатали 99% дыр в безопасности, которыми они посвистывали ещё лет 5-7 назад

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

может накидаю какие-то варианты

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

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

функционально неполную абстракцию над кодом

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

ORM в следствии своей парадигмы, толкает на написание работы с данными внутри кода, что приводит цельности логики и его дальнейшему рефакторингу.

Про безопасность - это вообще не к орм. Просто потому что орм - маппер. То, что он умеет генерировать sql, не накладывает на него обязанность следить за уязвимостями протоколов. И вообще обычно канал orm-db защищён по умолчанию.

ролевые модели в БД умеют генерировать

Кхм. С этого места пожалуйста поподробнее. Мне кажется, что мне вас есть чем приятно удивить.

Именно SQL ограничен декларативной парадигмой, а сверхсложную логику пишут на языках общего назначения

Срочно устраивайтесь в Databricks, а то они зачем-то Spark Dataframe API трансформируют на Spark SQL, а не наоборот, дилетанты!1! И всякие Iceberg, Hudi и другие профессиональные инструменты для работы с данными почему-то продолжают развиваться в парадигме SQL в первую очередь.

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

Если в вашем пузыре клепания CRUD-сайтиков никто не умеет в SQL на профессиональном уровне, это не значит, что он чем-то ограничен) Вполне себе Тьюринг-полный язык программирования

ORM в следствии своей парадигмы, толкает на написание работы с данными внутри кода, что приводит цельности логики и его дальнейшему рефакторингу.

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

Логика кода от его обфускации в ORM или без него не должна меняться или становиться более или менее цельной

То же самое с рефакторингом, только SQL переписывать, тестировать и профилировать куда проще

Вопрос - зачем вообще делать маппинг отношений в объекты в приложении?)

Мне кажется, что мне вас есть чем приятно удивить.

Страшно представить, как какая-нибудь SQLAlchemy справится с разветвлённой иерархией ролей в PostgreSQL, когда она на джойнах нескольких таблиц и базовых DML-операциях начинает спотыкаться)

Даже не хочу знать)

Это всегда решается на уровне приложения или процедурного языка (PL/SQL, T-SQL, PL/pgSQL).

Проблема в том, что этой кодогенерацией придется заниматься самостоятельно и api у нее будет кривой из-за скудных возможностей языков бд. А для большинства orm уже написаны кучи провайдеров под каждую субд и во многих языках уже есть dsl для написания запросов.

Я плохо знаком с мёртвыми языками типа Пыхи и ещё хуже с его ОРМ, открыл почитать документацию по Yii2

Ну и сразу нарвался на типичную ОРМ-логику - joinWith по умолчанию генерирует два запроса на получение датасетов: левый джойн первой таблицы со второй и выборку из второй таблицы с коррелированным подзапросом (where in (...))

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

Можно я не буду эту лажу воспроизводить?)

мёртвыми языками типа Пыхи

Смешно. Нет.

Ну и сразу нарвался на типичную ОРМ-логику - joinWith

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

возможно он вам и нужен дальше для каких-то целей

Нужен, название категории товара выводится потом на странице. Это кстати видно по тому, что можно задавать сортировку по названию категории. Данные выводятся таблицей, при нажатии на заголовок происходит сортировка по этому параметру.
Для менеджера joinWith в общем-то не нужен, я навскидку писал. Раз он в if, то можно считать, что он не выводится, и сортировку по нему делать не надо.

получаем ненужный и неоптимальный (бесспорно, это плохой запрос и общеизвестный антипаттерн) запрос к БД

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

Тут кстати еще вопрос, что будет быстрее, передача 100 дубликатов данных одной категории из базы в приложение, или второй запрос с WHERE IN с одним id.

Можно я не буду эту лажу воспроизводить?)

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

Прикольная статья, обязательно гляну как оно там устроено. Особенно понравилось описание в начале)

Я после первого

pip install SQLAlchemy

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

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

Но в последствии пришлось чуть поплотнее разобраться как устроены современные СУБД. (индексы, первичные ключи, реляционный связи, RLS).

Без всех этих знаний всерьез браться за ORM опасно.

Против ORM массово есть два возражения:

  1. Не знающий SQL всё испортит.

  2. Знающий SQL привык писать только SQL.

Первое устраняется принятием в команду грамотного специалиста. Второе устраняется принуждением написать два варианта - SQL и ORM, с полным пониманием, как оно внутри работает. Второй вариант обычно показывает, что понимания примерно столько же, сколько в варианте №1.

Вывод - вся критика ORM исходит от непонимания. И от нежелания понимать (это про фанатов чистого SQL в первую очередь).

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

В эпоху засилья микросервисного подхода, когда для получения данных из БД пихается сообщение в кафку, которая находит один из тысячи микросервисов, ответственный за выполнение select * from table1, затем отдаёт сообщение микросервису, который уже через сотню фасадов и прочего нагромождения (включая ORM) наконец где-то глубоко в недоступных потрохах передаёт SQL в одну из сотен баз данных (ага, по БД на микросервис), действительно оказывается, что добавление ORM к этому монстроидальному зоопарку ничего не меняет, кроме одного важного момента - без ORM нельзя копипастить готовые шаблоны простейших действий из интернета. Что возвращает нас опять к началу - мы имеем засилье самых примитивных погромистов вместо действительно необходимых специалистов.

Взглянув на такую схему получения данных из БД, пишущие на SQL действительно хватаются за голову и начинают ломать вокруг себя все 4-5 мониторов, которыми окружены как модница приблудами для покраски ногтей. Но граждане, вы не туда смотрите! Точнее - вы видите собачий бред, позволяющий за приложения уровня Hello World получать миллиарды денег. Но ORM здесь не при делах! Неуиноатый он, вот так.

ORM действительно ускоряет разработку. Но не модную, а эффективную. И если вам по жизни не повезло и вы не встречали на трудовом пути эффективные решения с ORM, то хотя бы не вставайте сразу в позу отрицания и вспомните, что в мире есть решения без микросервисов, кафки и прочего лютого треша. И это значит, что вы прошли мимо этих эффективных решений. Что в свою очередь означает - вам ещё учиться, учиться и учиться, много лет. Но ORM в этом не виноват. Это просто вам не повезло. Хотя знайте - так же как и вам не повезло ещё 95% погромистов, потому что когда на каждый приличный банк приходится по 1000 разработчиков, это означает, что 50 из них реально нужны, а 950 - всего лишь перекладывают json-ы из кафки в микросервисы, не создавая при этом ничего полезного с точки зрения логики бизнес-процесса. Так что сломайте свои мониторы не злобы ради, но понимания для. Поймите - инструмент ORM не виноват, а виноваты... ну вот такие у нас социальные условия, таков метод производства общественного продукта.

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

Я решил эту проблему кодогенерацией типов для запросов прямо из БД: https://habr.com/ru/articles/781982/. Если вкратце, то для каждого запроса автоматически генерируется функция-обертка, возвращающая нативные Python объекты. Т. е. остается только вставить аргументы в нужные места запроса. Такой подход значительно облегчает общение с БД, а также миграции схемы.

Python: https://github.com/racinette/querky (моя библиотека)
Kotlin: https://github.com/cashapp/sqldelight (похожая по идее библиотека, но запросы пишутся в отдельных SQL файлах)

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

Публикации

Истории