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

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

Да на здоровье. Пишите свой SQL, потом ещё SQL, который генерирует SQL и всю бизнес логику на SQL. И поддерживайте это всё, написать то ума много не надо.

ORM дает общую обертку над кучей разных СУБД, у которых часто разный синтаксис, несмотря на SQL92 стандарт.

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


С ORM код становится проще и понятнее, и поддерживает автоматические плюшки типа миграций и рефакторинга.


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

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

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


А потом еще замапить данные на объект окажется неплохо.


И вот у вас ORM.

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

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

Значит вам надо выносить это в отдельную функцию, как я и описал.
Запрос тоже можно вызывать с разными параметрами.
В итоге в коде это все равно будет вынесено в отдельную функцию, я не прав? Или вы будете хранить параметризированный запрос в глобальной переменной?
Разумеется это будет отдельная функция вы же этот запрос в 7 разных местах используете. Но что происходит в этой функции Вы знаете, и понимаете. А что там нагенерит ORM — бабка на двое сказала.

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


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

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

select index, name, state from user_address where state like 'U%' and index >= 6


Подскажите тут проблему?


База пусть будет PostgreSQL.

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

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


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


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


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

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

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

Для задачи минимального маппинга есть Jdbc template.
Он делает ровно то что должен и ничего больше. Мапит result-set на объект.

Дело в том что ORM поощряет доставать всю таблицу чтобы получить одно значение, а SQL — нет.

Мне кажется, вы говорите про какую-то конкретную ORM и распространяете ее проблемы на всех.


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


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


Потом получится, что вместо того, что бы писать условный блок кода:


with connection() as conn:
   query_raw_result = query.execute('select * from x where x.c > 5)
   record = mapping(query_raw_result)

Вы напишите свою функцию и привяжите ее к классу, в который мапиться результат. Потом таких функций будет все больше и больше — и после рефакторинга вы получите легковестный orm способный на простые select/insert/update, а все остальное вы будете писать пока сырыми запросами. Ну и оно будет немного расширятся.


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


Дело в том что ORM поощряет доставать всю таблицу чтобы получить одно значение, а SQL — нет.

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

Ничего что в вашем конкретном примере используется голый SQL? Более того конкретно этот пример будет нормально работает с любым ORM и его диалектом. А вы попробуйте написать запрос с 5ью джоинами и сложными условиями. Потом попробуйте повторить это на каком-нибудь hql или queryDsl. И не факт что с первого раза нормально получится.

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

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


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


Если вы используете ORM, у вас нет обязанности вот прямо все запросы писать на ORM, но большинство запросов, которые будут обычные select с парой фильтром или же insert вполне можно отдать ORM.

А для простых запросов не нужен свой язык запросов. Он точно так же может работать на SQL. Все что нужно от ORM — использовать нативный SQL и нормально мапить result-set на объекты.

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


То есть, вместо условного T1.objects.filter(pub_date__lte=5) мне стоит написать сырой sql запрос? Довольно много лишнего кода получится.


Плюс я еще могу писать что-то в духе T1.expired() как алиас для запроса.


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

Про другие языки я говорить не буду.
Но если вы все-равно мапите результаты запроса на ваши доменные объекты, то проверить соответствие этих доменных объектов структуре базы не представляется сложным. Можно даже использовать стандартные аннотации.
P.S. Все что я говорю относится к Java.

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


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


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

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


Отнюдь — habr.com/post/413597
Это ваши java/hibernate проблемы, правда. У нас вот на python/django таких проблем нет и пишутся модельки прямо из кода, потом создаются миграции и все это чудо няшно работает.

Ну и может я читал плохо, но я так и не нашел инструмента, который вы предлагаете использовать, что бы синхронизировать схемы базы на разных окружениях. Если этот инструмент «sql-скрипт» или «админ», то вас ждет очень много боли.
Это ваши java/hibernate проблемы, правда. У нас вот на python/django таких проблем нет и пишутся модельки прямо из кода, потом создаются миграции и все это чудо няшно работает.
А вы внимательно прочитали статью? Там вообще-то универсальные проблемы. Создаст ли ваш мигратор индексы? А в нужном порядке? А все ли он правильно сделает? А не снесет ли случайно колонку с данными? На проде между прочим. Поэтому тут я согласен, мигрировать базу можно только чистым SQL и никак иначе. В противном случае на то, чтобы проверить что мигратор реально ничего не поломал вы потратите раз так в 5 больше времени чем на написание честного SQL обновления.

Отбросив вопрос про "все ли он правильно сделает", который применим в целом в sql скрипту в том числе, могу ответить одной фразой — "там прямо написано".


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


А в дополнение к этому еще можно вести версионность и зависимость между миграциями.


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

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

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

Это что за база такая? Flyway умеет всё что умеет JDBC.

Это была кассандра и миграции на CQL. Для реляционных мы используем django orm и там это решается на его уровне.


Касательно flyway — он классный. но вряд ли есть смысл заменять то, что используется сейчас на него и pure sql, если у нас нет проблем, описанный в статье.

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

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

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

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

Ок, а как быть с
Как в ORM с миграциями когда надо добавить новую колонку, а заполнять надо данными из другой таблицы
?

А в чем проблема, опять же? У вас на выбор будет:


  • ДЛС для миграций
  • Сырые sql скрипты
  • Сырые скрипты на языке
  • Расчетные поля, которые рассчитываются в тот момент, в какой вы достаете модель кодом и сохраняются.

Лишить премии? Новая функция не будет работать, если нет данных. Если у вас в коде IF, который фактически отключает эту новую функцию, то можно было и не выкладывать.


По уму нужно сначала миграции, потом код.

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

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

Также code-first по своей сути исключает возможность cross database запросов, по крайней мере в мире .NET
А что вам мешает проектировать нормально на моделях? Отношения есть, индексы есть, модели, которыми нужно управлять самим — есть.
code-first применим когда проект новый, и прежнюю базу можно выкинуть и создать новую по шаблону из кода.
Как только в базе появляются данные — база становится легаси. Наличие *любого* Undefined Behaviour на вашем пути миграции означает, что базу придётся мигрировать вручную, т.к. ORM с высокой вероятностью в чём-то напортачит, а закрытый (может быть) баг в его процедурах миграции — слабое для Вас утешение.

(цитирую почти один в один вот отсюда, и практически целиком согласен с автором: blog.jooq.org/2018/06/06/truth-first-or-why-you-should-mostly-implement-database-first-designs)
Хорошая статья, на Хабре есть перевод
базу придётся мигрировать вручную, т.к. ORM с высокой вероятностью в чём-то напортачит

Вы же видите миграции ORM, как они могут напортачить?

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

Ну и вы скрипт миграции руками допишете. Что не так-то?

Что именно может напортачить ORM когда каждая миграция проверяется разработчиком?

В том то и дело, что нужен. Потому что каждый раз мапить вручную — это полный бред. Значит писать под каждый случай функцию, которая достанет данные и замапит. Какой практический смысл в этом?

Для .NET такое уже сделали и я даже пользовался им на небольшом проекте. github.com/StackExchange/Dapper
А вы попробуйте написать запрос с 5ью джоинами и сложными условиями.

А давайте попробуем.


$query = User::find()->as('u')->with('articles');
if ($cond1) {
  $query->joinWith('profile p');
  $query->andWhere(['<', 'p.age', 18]);
}

if ($cond2) {
  $query->andWhere(['=', 'u.type_id', UserType::EXTERNAL]);
}

$users = $query->all();

// ------------------
// User.php

class User
{
  ...

  function getProfile()
  {
    return $this->hasOne('profile', 'user_id', 'id');
  }

  function getArticles()
  {
    return $this->hasMany('articles', 'author_id', 'id');
  }
}

Как это будет с использованием SQL?


В результате должен быть список пользователей с заполненным свойством $user->articles, где находится список статей, написанных конкретным пользователем. Джойн естествено должен быть только если $cond1 == true, $cond1 и $cond2 зависят от параметров GET-запроса.

Это конечно псевдо-java-код но примерно так

StringBuilder sql = "Select articles from user "
if (cond1) {
join = ("join profile p on p.id = u.profile_id")
conditions.put("and p.age < :age")
}
if (cond2)
{
conditions.put("and u.type_id = :type")
}
sql = sql.append(join).append("WHERE 1=1 ")
condtions.foreach(sql::append)
SqlParameterSource namedParameters = new MapSqlParameterSource()
.addValue("age", 1)
.addValue("type": type);
return namedParameterJdbcTemplate.queryForObject(sql, namedParameters, User.class);

P.S. почему-то разметка поплыла не получается выровнять :-(

Ну во-первых, articles это отдельная таблица со своими полями. Во-вторых, вы как-то незаметно пропустили объявление 2 важных переменных, а это тоже дополнительный код, который надо писать, читать, и поддерживать. К тому же вам было лень писать SQL-операторы капсом, что тоже сказывается на читаемости. В-третьих, если я правильно понял, queryForObject возвращает один результат, а не список.


И даже если перевести ваш код в PHP, он все равно будет в 1.5 раза больше, чем код с ORM, и с увеличением количества WHERE и JOIN его будет больше. У меня 1 переменная для запроса, а у вас 4. И все их надо будет копипастить в другие места, где есть WHERE и JOIN. А если название колонки или таблицы в JOIN поменяется, то искать по всему коду и исправлять.

ну я пропустил 2 переменные, а вы их просто захардкодили.
Говорить про читаемость вот этого вот странного порядка я даже говорить не буду
$query->andWhere(['<', 'p.age', 18]);

капс исправить можно, а вот порядок не очень.
представьте что вместо query for object аналогичный метод для списка. Это не принципиально.
А какая разница сколько переменных если они инкапсулированы внутри метода. Это же не глобальные переменные.
Говорить про читаемость вот этого вот странного порядка я даже говорить не буду
$query->andWhere(['<', 'p.age', 18]);

Говорить на самом деле нечего — здесь все интуитивно понятно. Операция и два значения.
ну я пропустил 2 переменные, а вы их просто захардкодили

Так в том и смысл, что вам надо их объявлять, и по-другому никак) И у меня в строке не join, а название связи, а аналога conditions вообще нет, захардкожены только сами условия, как и у вас.


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

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


если они инкапсулированы внутри метода

Так речь о том, как поддерживать код метода. Естественно, если там никогда ничего не будет меняться, то неважно что там написано. Хотя написание тоже удобнее получается.

Так в том и смысл, что вам надо их объявлять, и по-другому никак)

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

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

ну то есть это по сути класс-генератор sql запросов. Уверен если покопаться в java я таких же десяток найду только использующих стандартный синтаксис.
Можно точно так же захардкодить.

Ну конкретно в этом случае можно заменить на конкатенацию, потому что в sql фактически только название таблицы, и ее можно считать началом джойнов. А если надо будет в SELECT поля по условию добавлять?


Правильно настроенный check-style скажет все что думает о таком коде.

Ну вот, не только дополнительный код писать, а еще и дополнительные инструменты настраивать. Зачем, в чем профит-то?


ну то есть это по сути класс-генератор sql запросов

Не только, потому что джойны заменяются на связи ( R ), описанные в классе, условие джойна генерируется по ним. Можно даже превратить в many-to-many без изменения вызывающего кода.
И вы игнорируете что articles это таблица. ORM по связям определит ключи, сделает второй запрос с IN, и заполнит свойство articles у всех объектов в списке users. Для SQL будет джойн с дублированием строк таблицы users или еще одна портянка кода c ручным распределением.


Уверен если покопаться в java я таких же десяток найду

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

А все эти ваши joinWith и andWhere будут передаваться в виде динамических запросов движку бд, тем самым затрудняя оптимизацию?
Не знаю, почему вы так решили, из них потом генерируется обычная строка с SQL-кодом.
Обычная динамическая строка типа WHERE Age < 18 или обычная строка вида WHERE Age < @ARG01, @ARG01 = 18?

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

А join на этот запрос вы сделать можете? А если там еще и группировка или урезанная по нескольким полям выборка?

Я веду к тому что в нормальных ORM используется не только фильтрация, а и в полный рост query decomposition. Склеиваете такие маленькие подзапросы, накладываея ограничения по доступу, и на выходе получаете взрослый оптимизированный запрос. Поверьте оптимизированный, намного лучше чем руками бы писал.

Сколько не смотрел на другие языки и их ORM, только .NET с его IQueryable позволяет такое делать типобезопасно и предсказуемо. Естественно зависит от linq провайдера. Мой выбор давно пал на linq2db
Там есть join и даже eager loading, select тоже можно указать если надо. Группировку мало смысла мапить на объект класса, обычно массив запрашивается.
применяет ту же логическую ошибку, что и по всей статье — у вас нет единого SQL


Т.е. ребята из ISO и ANSI с 83-го года работают в пустую?

Классно, правда?)


Вот стандартизировали они json и толку?

Согласен с вами, но нужно смотреть глубже. Как в одной из лекций говорил uncle bob, может быть база данных вообще не нужна? И приводил пример того, как они строили свое приложение не пользуясь базой и добавили ее только тогда, когда один клиент специально на наличии базы настоял.

А ссылочки у вас не завалялось?
Спасибо!

Не надо за ORM перепроверять. Если у вас запрос на 10 строк — пишите вручную, если хотите. А одну строку ORM сделает лучше и предсказуемее. Потому что защита от опечаток. Вы попробуйте просто.

Защиту от опечаток дает IDE, так-то.
Я пробовал. У меня прямо сейчас открыт метод на 200 строчек который пишет сложный запрос на queryDSL
Защиту от опечаток дает IDE, так-то

Дает до поры до времени. И у всех иде разные.
Когда вас будет несколько — вы тоже не очень будете знать, какой же там запрос написал тот человек.
А как же code review?

Вы всей командой его проводите? У нас только один человек.

Да
Вы классные, что я могу сказать :)

Сначала у вас функция, которая возвращает массив данных по клиенту. Потом вы захотите объект User. Потом, когда объектов много, вы захотите не делать под каждый объект функцию Something::getByID. Потому что зачем вам 50 одинаковых по сути функций?

Наверно, надо научиться пользоваться ORM?

НЛО прилетело и опубликовало эту надпись здесь
Есть же копромисс. Например JDBC Template
НЛО прилетело и опубликовало эту надпись здесь
Не совсем понимаю о чем вы говорите. Я не понимаю как иначе можно собрать доменный объект из resultSeta. Только последовательно заполнить все поля.
НЛО прилетело и опубликовало эту надпись здесь
JDBC Template точно так же избавляет вас от этой работы. Или я не понимаю о чем вы.
А мне кажется в этом утверждении скрыт софизм! При наличии ORM, у меня в 6 местах будет один и тот же код по генерации этого «SQL» запроса. И поэтому я тоже эту простыню вынесу в отдельный метод. И, по итогу, при отсутствии ORM у меня в методе будет понятный SQL-запрос, а при использовании SQL — будет того же объёма код, генерящий этот запрос в терминах ORM, и как уже написали ниже, до результирующего SQL ещё поди доберись.

Вообще, как по мне, так с ORM ты по итогу приходишь к тому, что оборачиваешь их ещё одним слоем абстракции как раз из-за того, чтобы не дублировать код построения запросов. И в итоге понимаешь что на самом деле тебе хочется просто удобно (за минимум манипуляций) мапить результат выполнения запроса на сущность, сохранив возможность писать чистый SQL
Ну, метод для генерации пишется довольно просто в одну строчку, в отличии от работы с сырым sql, где вам еще надо как минимум создать коннект и выполнить запрос + скачать данные.
В одну строчку? Появились ORM которые читают мысли? Любой запрос сложнее «загрузи сущность по ID» — всегда несколько строк. И чем сложнее запрос, тем больше строк нужно написать для ORM, в сравнении с текстом SQL запроса.

С сырым SQL — создать коннект, выполнить запрос? Ровно так же, как и с ORM нужно получить сессию и отправить запрос на выполнение. Ровно тот же самый код
Кода вообще может и не быть — одни аннотации. И я даже не помню, когда я последний раз включал лог sql кода.

P.S. У дядьки явно бомбануло, ему бы подорожничек приложить.
Что делать если ORM не поддерживает всех плюшек СУБД, потому что надо иметь один интерфейс для многих СУБД?

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

хорошего ORM
Как понять хорошии или нет?

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


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

Можно узнать что за ORM?
Django ORM уже научился в JOIN по двум условиям? или в JOIN не связанных через foreign key таблиц? или умеет генерировать SQL с WITH?
Django ORM хорош когда нужно сделать что-то простое, это и правда удобно. Когда же нужно сделать что то не стандартное то появляется ощущение что у тебя все колесо в палках.

И согласен, и нет.


Django is a quite opinionated framework. Со временем просто привыкаешь не писать поперек линованной бумаги, и количество палок резко снижается.


А вот с legacy database это полный ад, да.

«не писать поперек линованной бумаги» работает только когда в проекте только один Django пользуется базой и он же ее создает. В иных ситуациях код начинает состоять из кучи unmanage моделей и самописных SQL constraints. Кстати в текущую ветку Django вроде приняли патч добавляющий создание CHECK constraint из моделей.

В качестве дисклаймера: я, на самом деле, django orm не люблю. Автокомплит в разных редакторах от плохого до омерзительного, паршивая расширяемость, куча магии плюс сильная заточка под OLTP, так что при малейших поползновениях в сторону аггрегации/аналитики удобней откатиться в сторону raw sql.


Но вашей проблемы я не прочувствовал :)


Если миграцией базы управляет django, то есть https://github.com/rapilabs/django-db-constraints, ну или на худой конец sql прямо в миграциях.
Если база управляется извне, то в чем проблема c unmanaged моделями-то?
Если же и так и так, то в проекте есть проблемы посерьезней, чем выбор орм =)

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

Еще большинство ОРМ сами санитайзят параметры, т.е. бесплатная защита от инъекций.
Самому за этим везде следить — бОльшая вероятность допустить ошибку.
0_о это привет родом из 90ых что ли? В любом нормально языке есть встроенный механизм защиты от SQL-injection. Для Java например это prepared statement. Говорить про sql-injection в 2018-ом просто неприлично.
Ну про в любом нормальном языке — это вы загнули конечно, но в любой нормальной библиотеке — согласен.
Действительно проблема уже не очень актуальная, тем не менее до сих пор периодически натыкаюсь.
>Для Java например это prepared statement

И как сделать ps для SELECT когда количество входных параметров варируется? :)

Кстати, criteria API для РСУБД тоже монстр, в mongo API всё на порядок продуманнее. Ещё и транзакции прикрутили в Монго 4.

Итого сейчас муки выбора sql vs orm почти не возникают, т.к. почти всегда больше плюсов у mongo. Но ретрограды, конечно ещё не все умерли :)
В монге, кстати, тоже инъекции бывают, особенно при использовании языков с динамической типизацией (таких как javascript). Буквально недавно тут пост про них был…
>В монге, кстати, тоже инъекции бывают, особенно при использовании языков с динамической типизацией

Уверен, что через java mongo API ничего такого в принципе не сделать. А проблемы вызывает обезьянний JS-код с использованием eval и прилепленными входными данными. Но это уже никак не проблема Монго.
Ну, я так понимаю, достаточно JSON.parse(), никакого eval не надо.

const { name } = JSON.parse(query);
userModel.findBy({ name: name });


Вот в этом коде может быть инъекция
Возможно неправильно понимаешь :) по крайней мере в статейках с высосанными из пальца примерами траблы вызывает именно JSON.parse() + eval.

В твоем примере, допустим query пришло извне и никак не проверяется. В итоге, оно либо распарсится в объект, либо не распарсится. Где инъекция-то? :)

Вот тут: { name: name }. Автором кода предполагается, что name — строка, но реально тут может быть запрос любой сложности.


Возможно, что утечки данных и правда не произойдет — но DoS через километровый запрос организовать можно и попробовать.

В твоем примере, допустим query пришло извне и никак не проверяется. В итоге, оно либо распарсится в объект, либо не распарсится. Где инъекция-то? :)

Ну ладно. Представьте такой код:
const { name, token } = JSON.parse(query);
userModel.findBy({ name: name, token: token });

И вот пользователь вместо
{ name: 'Вася', token: '123e4567-e89b-12d3-a456-426655440000' }

Передает такое:
{ name: 'root', token: { $ne: '1' } }

Догадываетесь, какой будет результат?
>Догадываетесь, какой будет результат?

«прогера» будут больно пинать ногами на первом же ревью?

Я даже у джунов никогда такой откровенно притянутой за уши ереси не встечал. Это не аналог SQL-инъекции, а из разряда взять и полностью выполнить SQL запрос пришедший с веба. Таким образом можно вообще любую СУБД положить, если делать максимально идиотские вещи.
Ясно. Ну значит, инъекций нет. Монго — безопасная, а весь существующий софт — без уязвимостей, все джуны — проходят ревью, а синьёры — не допускают ошибок, а я — пошел кормить своего маленького единорожка.
>Ну значит, инъекций нет

Нет инъекций. Почитай для начала что это такое.
Когда findBy(блабла) сделает insert(блабла) тогда и приходи.
Синдром утенка от инъекций? Инъекция — это обязательно insert? Повышение привилегий или произвольное чтение — это тоже инъекция.
В mongo java api нет вообще ничего из перечисленного, т.к. если в String засунуть { $ne: '1' } это просто не заработает.

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

А ты сказал, что только если eval использовать. А я доказал, что это не так. Так зачем тут распинался, если тебя не волнует?

У тебя корона как у Джобса, а знаний и опыта как у краба. Подтянись немного перед тем, как в такие споры вступать и научись признавать свои ошибки.
Какой настырный. ОСОБЕННО или ТОЛЬКО?
Если дыра у JS-макак, не надо со своей больной головы на Монго перекладывать.

Да у меня опыта в JS как у краба. И слава богу. И надеюсь не добавится.
Эта проблема возможна и на других языках если объектные модели используемые при десериализации JSON и при формировании запросов к монге случайно окажутся совместимыми между собой.

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

Ты — самовлюблённый сноб, ты знаешь об этом? Питон, кстати, имеет точно такую же уязвимость. И, главное, в Джава есть пакет com.mongodb.util.JSON, который точно так же позволяет произвольный JSON привести к Монге.

DBObject dbObject = (DBObject) JSON.parse(
    "{ name: 'root', token: { $ne: '1' } }"
);

Скрытый текст
пс. Да, я знаю, что тут неправильные кавычки. Но мне влом для Джавы их выставлять
Мне это обсуждение напоминает так называемые вирусы под линукс. Читаешь новость, удивляешься, качаешь, компилишь, не компилится, патчишь, не работает — ах, чёрт, надо было ещё SELINUX выключить и из под рута запустить. И тогда дырявый линукс повержен.

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

Это особенности использования. Если я буду ехать на спорткаре на полной скорости и специально въеду в дерево — это ведь не ошибка создателей спорткаров. Но я могу разбиться на спорткаре и это факт.

Так само и здесь. Монго — прекрасен, но любому, кто его использует важно понимать, что он не прикрыт со всех тылов, а он может наглупить и создать уязвимость в своем приложении
>кто его использует важно понимать, что он не прикрыт со всех тылов

ОК, открытие недели: если в Монго запустить некий код, этот код что-то сделает. Британские учёные отдыхают!
Вашу дешевая демагогию применяйте на детсадовцев.

Если в Монго передавать неотфильтрованный ввод пользователя, это может вылиться в уязвимость в вашем ПО. Это называется инъекция. Именно об этом мы и говорили. И именно о такой возможности вы еще пару дней назад не догадывались, а теперь стараетесь построить хорошую мину при плохой игре
Единственное что я узнал из этой ветки — js-макаки совсем опухли и пихают напрямую dto в Монгу. При этом почему-то считают, что делаться будет не то, что в dto. Нормально чо…
> Единственное что я узнал из этой ветки — js-макаки совсем опухли и пихают напрямую dto в Монгу.

Джава дебилы совсем охуели…
Джава дебилы совсем охуели…

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

Единственное что я узнал из этой ветки

То, что ты не способен к обучению и познанию — я уже понял. Но тут нечем гордиться

Если считаешь, что это уязвимость в Монге

че за бред то? В каких вообще дб есть уязвимости к инъекциям? Ни в каких. Уязвимости в коде.
Я могу ошибаться, но Jdbc Template решают эту проблему с помощью именованных параметров.
А вы знаете, что prepared statements локальны для каждого подключения к БД и что этих подключений может быть много и как только нужно создать ещё одно, все эти выражения «подготавливаются» и это может занимать ощутимое время?
А они не кэшируются?
Они вообще видимы только в пределах одного подключения к БД. В его пределах они кэшируются.
Так подключения тоже кэшируются(connection pool)

Во всех приличных СУБД защита от инъекций уже встроена. Используйте биндинги и враг не пройдёт.

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

Меняем не часто а вот добавить новую — это вот бывает. У меня например через Eloquent безшовно связаны 2 таблицы одна из mysql а другая из postgres.


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


Напрмер вытянув первую сущьность я могу через нее вытянуть вторую и на лету обновить её, хотя вытянул из postgres а обновил в mysql и все это 3 строки кода.


Запросами вышло бы на 20 без отлова ошибок.

А как вы проводите юнит тестирование запросов?
НЛО прилетело и опубликовало эту надпись здесь

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


Следовательно пост не имеет никакого смысла.

Следовательно пост не имеет никакого смысла.

Странный вывод. Вспомнился текст из защиты Чуббаки
Я вполне понимаю автора. Он хочет sql-подобный язык запросов. Он готов почитать специфику, но основная идея написания запроса должна быть похожая. И это есть в SQL, выборка пишется одинаково на любом диалекте, поддерживающем SQL92. Да, есть специфичные конструкции или функции, но они, как правило, расширяют стандарт. В случае с ORM, язык запросов часто настолько своеобразен, что без документации что-то написать сложно. Для примера, в мире Java есть Criteria API и JPQL. Последний сделали максимально похожим на sql, видимо была такая потребность. Наверное, дело вкуса, но читать даже небольшие запросы на Criteria API очень тяжело.

Так уж повелось, что в мире Java все, что имеет официальную спецификацию JSR, неудобно и эстетически неприятно. Зато сторонние библиотеки типа QueryDSL исправляют ситуацию. В большом проекте подобные type-safe врапперы сильно спасают ситуацию, например, когда нужно найти все места, где используется какое-то поле или таблица.

Мне кажется это очень похоже на SQL:
List<Person> persons = queryFactory.selectFrom(person)
  .where(
    person.firstName.eq("John"),
    person.lastName.eq("Doe"))
  .fetch();

Понятно, что синтаксис не один в один, но по логике формирования вполне. QueryDsl — это такой компромисс между типизацией из CriteriaApi и читабельностью SQL/JPQL.
Угу только например тот же QueryDSL очень любит делать cross join по поводу и без. И без дебагера вы это не узнаете.
Дык не используйте его, пишите на нативном sql. Это можно сделать как в рамках queryDsl, так и вообще без ORM. Вы про другую проблему — проблему использования ORM в принципе. ORM не научилась угадывать мысли разработчика до конца и генерить идеальные запросы.
Ну то есть инструмент для генерации запросов генерит плохие запросы. Зачем тогда нужен такой инструмент?

Потому что куча программистов напишет запрос еще хуже?)

Спорное утверждение :-) SQL как-минимум удобнее писать и тестировать. IDE с удовольствием выполнит любой ваш SQL запрос, а вот если вы попробуете ей скормить какой-нибудь hql…

У девятой java вроде есть repl режим и можно интерактивно выполнять запросы и смотреть, что происходит.


У нас на python так сразу есть shell режим где можно оттестировать абсолютно все.

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

Не, не так. Вот так: инструмент для упрощения работы с БД, который позволяет не писать много SQL кода и в большинстве случаев делает это неплохо, иногда генерит неоптимальные запросы. Так вот для многих задач это не критично. А там, где критично, надо тюнить или использовать нативный запрос. Как-то так.
Проблема не в том, что он строит не оптимальные (не корректные) запросы. А в том, что ты не узнаешь, что он не корректный пока не отстрелишь себе колено по пояс.
Да, человек смертен, но это было бы ещё полбеды. Плохо то, что он иногда внезапно смертен, вот в чём фокус!
Есть такая фигня, тестирование называется…
Есть такой параметр, называется сложность тестирование.
И если проверить sql запросто достаточно просто, то с запросо-генерацией все обычно чуть более запутанно. Да что там говорить, в гибернейте каждый релиз фиксят тонну багов. Я что-то не помню такого количества багов в SQL.
И если проверить sql запросто достаточно просто, то с запросо-генерацией все обычно чуть более запутанно.

В чем запутанность то? Включаем в опциях логирование запросов, получаем все sql запросы с параметрами. Дальше процедура ничем не отличается от sql запроса. В чем сложность-то?
Полагаю просто, что в простоте тестирования sql есть лукавство. Его более-менее легко тестировать, когда ты сам его только что написал. А когда это другой человек и прошло пару месяцев с момента написания, тогда все будет обстоять ровно также как и с sql от ORM.
И вот я делаю code-review и чтобы убедиться, что человек правильно написал ORM запрос мне нужно запустить среду со всеми флагами. Прямо в браузере из гита это сделаю, ага.
Нормально написанный SQL нормально читается и через день и через месяц.
Я же не говорю, что ORM плохо читается. Читается то он замечательно. Но не всегда делает то, что написано.
Читается то он замечательно

Вот тут я бы поспорил, далеко не всегда.
Но не всегда делает то, что написано.

Делает то. Просто не всегда именно так, как вам хочется. Т.е. проверить запрос ORM именно на правильность несложно прямо в браузере, в code review (особенно какой-нить jpql).
Но в целом, я понял вашу позицию, вам просто не нравится ORM или он не подходит для ваших сценариев использования. Тогда просто не стоит его использовать и мучатся.
убедиться, что человек правильно написал ORM запрос

Можно человека попросить прислать вам что он нагенерит.
Ну и в 99% случаев для более сложных запросов используют «голый» sql.
И кроме того, вы достаточно уверены что во время review вы заметите где-то опечатку или то что все параметры правильно 'заэскейпены'?
я где-то внизу уже писал про «эскейпинг». Если вы в 2к18-ом думаете об «эскейпинге» — в вашем проекте что-то не так.
Возможно имелся в виду не совсем эскейпинг, а вставка параметров в запрос. По крайней мере это то, что не нравится мне в чистом sql. Вот пример с jdbcTemplate:
String sql = "INSERT INTO CUSTOMER " +
			"(CUST_ID, NAME, AGE) VALUES (:custId, :name, :age)";
			
		Map<String, Object> parameters = new HashMap<String, Object>();
		parameters.put("custId", customer.getCustId());
		parameters.put("name", customer.getName());
		parameters.put("age", customer.getAge());
			
		getSimpleJdbcTemplate().update(sql, parameters);

Тут все параметры типа Object. т.е. в age можно передать строку и это упадет только в runtime! И это еще запрос хорошо написан, с именованными параметрами, обычно для параметров просто используют вопросики и нужный порядок параметров. Только не надо говорить, что для этого есть код-ревью.
Возможно имелся в виду не совсем эскейпинг, а вставка параметров в запрос.

Честно говоря я имел в виду именно эскейпинг, но валидация параметров — тоже неплохой пункт. Удобно когда поля уже заданы в модели и не надо об этом париться.
я где-то внизу уже писал про «эскейпинг». Если вы в 2к18-ом думаете об «эскейпинге» — в вашем проекте что-то не так.

В нашем проекте совершенно точно «что-то не так», но не все проекты идеальные. Согласен что это не очень важная вещь, но лучше с ней чем без нее.
И кроме того, вы достаточно уверены что во время review вы заметите где-то опечатку или то что все параметры правильно 'заэскейпены'?
Для этого есть тесты
НЛО прилетело и опубликовало эту надпись здесь

Я вот как раз сторонник вызова цепочки методов типа .select().where().where().fetch() вместо написания чистого SQL, особенно когда условия в where собираются динамично, т.е. может быть одно, а может и семнадцать, плюс пара джойнов, но красиво и понятно это выглядит только на простых запросах. Как только что-то чуть сложнее пары вложенных AND и OR с JOIN, так, как тут выше уже писали, без дебаггера и документации уже не разобраться, и быстрее выйдет написать SQL.

Вот поддержу! Построитель контролируемых запросов и маппер полей результата на объекты — действительно полезные инструменты.
Закулисная магия, порождающая медленных уродцев, богомерзкие актив рекорды и прочие протекающие абстракции годны лишь для прототипирования, да на личные поделки авторам ORM, на 100% понимающим как оно там внутри работает.
У остальных же кодеров, зачастую, отнимают больше времени, чем экономят.
Смотря на каких проектах. Для highload-проектов возможно ORM — не лучшее решение. Но там и денормализацию приходится делать и много чего ещё.

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

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

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

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


Есть еще офигенная библиотека JINQ, но она сейчас находится в standby. Хоть и рабочая, но с грядущими изменениями в JDK что-то боязно ее использовать в серьезном проекте.

Ну вот автор пишет, что ему не понятен синтаксис mongo, хотя в основе там лежат абсолютно те же самые операции insert, select (называется find), update, delete.
Что ему не нравится? Синтаксис вызова этих операций?


И это есть в SQL, выборка пишется одинаково на любом диалекте, поддерживающем SQL92

Вы не правы. Например, прочитайте, например, вот это. Между тем же PostgreSQL и MySQL существует приличное количество различий, которые будут мешать. В этом и смысл моего комментария — стандарта SQL не существует и вам нужно каждый раз учить новый диалект SQL, когда вы работаете с другой базой данных. И разница с NoSQL базами данных лишь в том, что они честно сказали, что сделают свой язык запросов, потому что эмулировать sql и потом отходить от стандарта во всех местах для них не имеет никакого смысла.


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

Вы не правы. Например, прочитайте, например, вот это. Между тем же PostgreSQL и MySQL существует приличное количество различий, которые будут мешать.

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

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


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


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

По вашей ссылке ровно то, о чем я писал выше — отличие в деталях. Это проблема стандарта, который либо описал неудобное решение, которым никто не пользуется и делает свои, либо закрепляет что-то слишком поздно, когда пользователи уже привыкли к другому.
По nosql на самом деле не все так очевидно. Не всегда можно/стоит натягивать SQL на то, что под него не задумывалось. И тут я не понимаю, почему автор критикует mongo. Но в случае некоторых ORM замечания вполне адекватны.

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

Я вполне понимаю автора. Он хочет sql-подобный язык запросов

Не-не-не. Посмотрите внимательнее на примеры, которые автор критикует. Минимум три из пяти — SQL-подобные языки и он их называет свалкой

Вот да. Более-менее общепринятых стандартов SQL было несколько (89, 92, 99, 2003, 2008, википедия подсказывает, что есть еще 2011, и 2016). Причем, например, СУБД не может быть полностью быть SQL92-compliant и SQL99-compliant: стандарты противоречат в некоторых нюансах. Я из любопытства читал драфты этих стандартов — там изрядное количество какого-то лютого трэшака, который не реализует и не использует никто. Я не знаю, сколько надо выкурить самокруток из книг Дейта и Кодда и чем их надо забивать, чтобы придумать этот фарш. Ах, да, стандарт этот найти бесплатно не так-то и просто. Видимо из-за это оторванности от практики, закрытости и объёма этот стандарт никто не использует (в отличие от спек C++, C#, Java, ECMAScript и т.п.)
Я вряд ли ошибусь, если скажу, что 99% "SQL-совместимых" СУБД это MySQL(+Maria), PostgreSQL, MS SQL Server, Oracle и SQLite. Да-да, я понимаю, что есть еще кучка древних и/или нишевых (IBM DB/2, Sybase/SAP ASE, Terradata, MS Jet, H2, Firebird и прочая экзотика, плюс стали появляться облачные движки), но основная часть всё-таки первая пятёрка. Так вот, даже в этой пятерке шаг в сторону от SELECT name FROM users WHERE age > 18сразу приводит к несовместимости. Именно поэтому поддержка нескольких СУБД не так-то и распространена, особенно без ORM. И, наверное, ни одна из них не реализует стандарт SQL.
А как эти СУБД "реализуют стандарт SQL"? А по принципу шведского стола: СУБД выбирает чуть ли не до абзаца чему именно соответствовать и говорит "я поддерживаю SQL". На этом фоне MS C++ просто образец дисциплинированности реализации стандарта.
И вишенкой на торте: у самих СУБД между версиями весьма большие различия.


Так что, да, "стандартный SQL" это миф.

НЛО прилетело и опубликовало эту надпись здесь
Мне кажется, автор оригинала троллит.

ORM — это безусловно зло, но зло меньшее из возможных. Вопрос не в поддержке замены БД в любой момент, а в ортогональности системы типов любого языка и любой СУБД, не говоря уже о «стандартном» SQL, где она весьма маловменяемая. И в любом случае приходится делать трансляцию между этими несовместимыми системами типов каким-то способом.

И если не нравится, что ORM слишком умничает/тупит, то для своего проекта можно же и собственную прослойку навалять, причём такую, чтобы и нативный SQL допускала. Мне вот однажды нужно было специализированную под bulk inserts, так я написал, использую иногда.

Не факт. Я вот чем дольше программирую, тем меньше мне нравится концепция ORM… Она из серии "натянуть сову на глобус"… В реляционных БД нет никаких объектов и чтобы написать хороший запрос надо про объекты вообще забыть, а не пытаться как-то методы ORM в цепочку объединить. Да, само собой, в тривиальных случаях ORM работает хорошо, но если у вас что-то посложнее банального CRUD, то вам по-любому нужно знать SQL и проверять что там ORM сгенерил, а заставить ORM сгенерировать нужный запрос бывает тем ещё аттракционом.

В итоге — протекающая абстракция.
надо про объекты вообще забыть, а не пытаться как-то методы ORM в цепочку объединить.

C SQL вы будете не методы ORM в цепочку объединять, а условия для WHERE и таблички для JOIN-ов в массивах собирать. Во вложенных.

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

Так почему бы не использовать ОРМ для тривиальных случаев и рукописные запросы (которые практически во всех ОРМ поддерживаются) для нетривиальных? Учитывая что подавляющее большинство запросов — как раз тривиальные, то только профит.

Учитывая что подавляющее большинство запросов — как раз тривиальные, то только профит.
Каждый тривиальный запрос со временем может превратится в не тривиальный, и что, переписывать каждый раз?
Каждый тривиальный запрос со временем может превратится в не тривиальный, и что, переписывать каждый раз?

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

Как будто вам не придется переписывать сам запрос когда он меняется с тривиального на нетривиальный :-)

В целом я согласен с изложенным, хоть и на мой взгляд притянуто немало лишнего. ORM по мне вообще должен всё реализовывать через компилируемые функции, иначе, на мой взгляд, это будет вообще не ORM. Но вот хороших ORM и впрямь не хватает. Был вариант отображения хранимых процедур в EF через XML-описание, но потом от последнего варианта отказались, а в поддерживаемом теперь варианте в самом коде отображение хранимых процедур ушло на вечность в backlog. Про Hibernate не интересовался в последние годы, может хоть он лучше.


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


Что за ORM подразумевался здесь, мне интересно, кстати. Может, тот самый Hibernate.

Речь про NHibernate, не знаю, есть ли у него большие различия с Hibernate =)
В удобстве использования на моей памяти всегда были, поэтому под .Net я предпочитаю EF. Но уж для Java он вряд ли может быть в чём-то хуже, чем NHibernate, разве что известное неудобство с value type в Java.
Не слышал, чтоб хоть что-то из этого входило в стандарт SQL, а запросы в XML и JSON нынче стандартизированы лучше, чем SQL.

C 2003 года XML точно входит: https://en.wikipedia.org/wiki/SQL/XML
А вот SQL/JSON вроде только в 2016 году в стандарт добавили.

Пожалуй с точки зрения абстрактной архитектуры вы правы, но с точки зрения бытового применения — это будет тяжеловато тянуть, потому очень быстро начнут все городить костыли. Как собственно оно и случилось.
Имхо тут дело даже не в тех, кто не знает и не хочет знать SQL, пытаясь что-то сделать через ОРМ, с такими и так все понятно. Имхо есть другая проблема — ряд людей, которые пытаются в реляционную базу пихать все подряд, старательно не замечая другие подходящие инструменты. Вот там да, без ОРМ все становится еще печальнее, однако ОРМ имхо в таком случае является костылем, прикрывающи основную проблему.
Хабы, куда приписан пост интересны: и «SQL», и «NoSQL». По тегам в дальнейшем будут искать — и находить этот пост, где про NoSQL (почти) ни слова, кроме как упоминание, что свалка данных без стандартного языка запросов — это зло. Странно, что в списке нет «системного администрирования» — как же, ведь БД крутятся на серверах, а ими занимаются системные администраторы! А заодно «сетевой безопасности», которую тоже можно притянуть за уши.

Давайте делать посты не поста ради, а так, чтобы позже их можно было найти и использовать по делу?
А в чем противоречие? NoSQL понятие широкое. Весь пост посвящен SQL'ю и всяким альтернативным «гибким» DSL (читай NoSQL).
Ну, посвящен он отсутствию SQL, а не NoSQL. А вот хаб на Хабре — понятие понятной «ширины», он для постов, где пишется на некоторую тему.

"У рыбы нет шерсти, но если бы была, то там жили бы блохи..."
Ну как бы DSL, особенно в виде ОРМ под которым SQL, это как бы нифига не NoSQL, на сколько мне кажется. Каким определением понятия NoSQL вы руководствуетесь?
В NoSQL из SQL выкинули джоины, добавили поиск по блобам. Поскольку получившееся нечто формально SQLом назвать уже нельзя, а других приличных слов в голову не приходит, решили добавить «No» — так загадочнее.
Еще некоторые стесняются схемы «ключ-блоб» и с умным видом говорят: «а у нас тут вообще никакой схемы то и нет».
В RethinkDB, MongoDB вполне есть джойны…
Не берусь рассуждать что там с ORM vs SQL, но разные *QL у «всех этих продуктах SaaS» существуют по той же причине, что и разные языки программирования: они предназначены для решения разных задач. Ну и кагбэ глупо требовать сделать «как в SQL-92» от NoSQL-СУБД с императивным языком.
Ага, причем большинство из примера — даже не реляционные базы. Но видать человеку не хватает в жизни JOIN'ов
я должен изучить 34 разных ORM
А еще все эти продукты SaaS

Что-то все в кучу. Каким боком Adwords это ORM?

Как мне кажется тут речь идет о сильно раздутых api, в которых придуман чуть ли не свой специальный язык запросов, хотя под такой функционал вполне подошел бы обычный sql.
Как насчёт ORM, нативной для языка, которая делает запросы типо-безопасными и защищает от тонн ошибок человеческого фактора в компайл-тайме (разумеется в этом случае ваш ЯП должен быть не из из разряда «garbage programming language», вроде javascript или php)?
Что это за ORM?
Попробовали б вы так в мечети на HN…
Автор не хочет учить все ORM. Я не хочу учить все подводные камни всех баз данных. Но ни ему, ни мне это и не нужно. Пара фреймворков и пара баз данных — вот реально необходимый в работе обычного специалиста объем знаний. А потом, всегда хочется абстрагироваться от реализации в пользу бизнес логики, сделать код более читаемым и удобным для написания. Я не хочу работать с конкатенацией строк каждый раз, когда составляю запрос. И я хочу автодополнение кода в IDE, когда пишу свои запросы. Я привык к прогрессу, и хочу пользоваться предлагаемыми им плюшками.
Автор не хочет учить все ORM. Я не хочу учить все подводные камни всех баз данных.

Проблема вот в чем. Автор не хочет учить ORM и может справиться без этого.
Но вот лично вас подводные камни баз данных достанут в любом случае (на промежутке времени стремящемся к бесконечности).
Потому что главное свойство абстракций — они протекают. И не сможет вас ORM от всего прикрыть, более того еще и своих проблем докинет для счастья.
P.S. Idea замечательно умеет автодополнение для SQL.
Любой инструмент хорош применительно к чему-то. ORM мне хватит для решения большинства повседневных задач. Если появится задача, которая требует написания чистого SQL, я напишу этот запрос на чистом SQL. Потом оберну это в абстракцию на языке используемого мной фреймворка и задокументирую, почему пришлось так нестандартно поступить. Но, повторюсь, это — исключение.
Если вы разрабатываете один — подход вполне себе жизнеспособный. Если вы в команде, то очень быстро приложение начинает жить своей жизнью. И вот уже кто-то разобрался в том, как воспользовавшись полудокументированым способом оптимизировать запросы для ORM (пример из реальной жизни с Hybernate) и начинает через это делать все. А кто-то другой поверх ORM нагородил Repository, а третий сверху построил MVC и еще один слой абстракции на JavaScript для клиентской стороны. И вот уже смена длины поля становится задачей на несколько месяцев.
Вот кстати никогда не доводилось видеть продуктов где вопрос решался бы через структуру проекта. Я пробовал делать на своих pet-projects что-то вроде
src
+--sql
+--packageXx


и в sql складывать .sql файлики которые собирались как встраиваемые ресурсы. Таким образом их можно было отдельно тестировать и обеспечивалась связка с конкретной версией бинарника. А код работы с базой был в виде ВыполнитьЗапрос(ПолучениеЗаказовПоИд).

Может быть кто-нибудь уже пробовал так же делать и может рассказать связанные с этим подходом грабли?
Я так не делал, но давно собираюсь
НЛО прилетело и опубликовало эту надпись здесь
мода, такая мода… не, ну соль конечно тоже есть, но повальное увлечение проходит… толстые клиенты -> тонкие клиенты -> толстые клиенты
Начнем с ORM. Их основная фича — это сокращение времени разработки

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

Нормальные* ORM тоже позволяют работать с множествами…
НЛО прилетело и опубликовало эту надпись здесь

Я стесняюсь указать на слоника притаившегося в комнате, но…


Во-первых ORM в большинстве своем это таки DSL (domain specific language, где предметной областью является C(Q)RUD). То, что семантика распихана по нескольким файликам, часть из которых на привычном ОО-языке, не более чем попытка не слишком пугать программиста. Тот же Hibernate нормально преобразует все эти удобства в весьма формальную модель и генерит кучу байткода на лету, существенно не отличаясь от DSL компилятора.


Во-вторых SQL как бы не работает с любой произвольной структурой данных. Как минимум требуется (даже логически) первая степень нормализации, чтобы можно было вообще произносить слово "таблица", а для слова "ключ", в его нормальном понимании, хорошо бы иметь как минимум вторую. Упомянутые вами Splunk и Lucene как раз специфические языки и придумывают от того, что "поток частично структурированных сообщений" и "некоторое количество коллекций гетерогенных слабо-структурированных документов" несколько не ложатся на любимые всеми таблички.


В общем не надо бояться языков, тем более что большинство из DSL-ей простые как дрова. Если вы справились с SQL, маппингом в ОО, транзакциями через аспекты и реляционной алгеброй, все вариации на тему "100500 способов описать логический фильтр" для вас проблем не составят.

Про ОРМ — настолько феерический бред, что не могу не отметиться.


Все звучит красиво, пока твои потребности умещаются в select * from table. В реальности же… ну, давайте на примере DSL Django ORM (первое, что в голову пришло).


queryset  = User.objects.all()
if len(statuses) > 0:
  queryset = queryset.filter(status__in=statuses)
if last_comment:
  queryset = queryset.filter(user__post__comment__сreated_at__gt=last_comment)
if signed_in_from:
  queryset = queryset.filter(signed_in__gte=signed_in_from)
if signed_in_to:
  queryset = queryset.filter(signed_in__lte=signed_in_to)

cnt = queryset.count()
admins = queryset.filter(is_admin=True)
data = admins[:100]

Знакомая ситуация? Думаю, да. Неужели кому-то по кайфу каждый такой запрос обписывать этим всем ворохом " AND ".join(chunks) и прочей бесполезной белибердой????

Я, как довольно активный пользователь ActiveRecord ORM (рельсы), могу сказать, что этого хватает на процентов, эдак, 95 всех задач. Он действительно хорош. Ещё процента 4 покрывает Arel — SQL AST, и, пожалуй, оставшийся 1 — это что-то совсем-совсем сложное, что нужно писать полностью руками.
Но это всё не отменяет того, что нужно хорошо понимать особенности работы БД, причём довольно глубоко
Согласен с автором по многим пунктам относительно ORM. Их слишком часто применяют «по-умолчанию», как еще недавно делали с XML (ну теперь пошел JSON) и считают серебряной пулей. Однако не согласен про DSL.

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

Если точнее то имеем GKE + Stackdriver + json logging + export to BigQuery + BigQuery.
Поиск по логам в Stackdriver Logging. Если нужны какие-то агрегации или статистика за большой период времени то в BigQuery.
Между “чистым sql” и ”orm” есть еще один промежуточный слой, который мало где реализован (может быть, ближайшее приближение — LINQ).

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

Если же sql был бы нативной частью языка (да еще поддерживал бы условные джойны или условные добавления where-выражений), плюс имелся бы слой, который из “select *” сразу возвращал бы объект предметной области, а не «ассоциативный массив», то и получилось бы то, что хочет и автор, и комментаторы: с одной стороны, это чистый sql (ну почти), а с другой, отсутствует boilerplate (прямо как в orm).

Много лет назад я пытался что-то в этом направлении делать (а именно, язык условного построения джойнов и where-выражений без склейки строк), получилось github.com/DmitryKoterov/DbSimple/blob/master/README.txt (см. там {}-конструкции и плейсхолдеры типа ?a, в них самая соль). Но это все очень старое и было давно, плюс оно по-прежнему оставалось строками (хотя строками уже совсем другого рода, практически атомарными), плюс не было слоя возврата/сохранения объектов, типа как $mapper->save($object) или $object = $mapper->get($id).

Пример оттуда для иллюстрации идеи:

$rows = $DB->select('
        SELECT *
        FROM 
            goods g
          { JOIN category c ON c.id = g.category_id AND 1 = ? }
        WHERE 
            1 = 1
          { AND c.name IN(?a) }
        LIMIT ?d
    ',
    (empty($_POST['cat_names'])? DBSIMPLE_SKIP : 1),
    (empty($_POST['cat_names'])? DBSIMPLE_SKIP : $_POST['cat_names']),
    $pageSize
);

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

www.jooq.org
У господина автора просто узковатый кругозор. Плюс мания величия, раз указывает целой пачке архитекторов что им надо делать.

Вообще, когда некто гребёт все NOSQL в одну кучу, сразу становится понятно его место в жизни :) Скорее всего его дальнейший полёт мысли заключается в том, что NOSQL и ООП не нужны, есть ведь могучий SQL и какой-нибудь Cobol!

По существу: пусть прикрутит к neo4j свой SQL. Или к Cassandra, а потом рассказывает как всё предсказуемо и прозрачно работает.
У Cassandr'ы же есть почти себе SQL
У Каси data model насколько своеобразная, что даже если бы CQL был SQL-ем (что совершенно не так) РСУБДшник со своими РСУБДшными понятиями ничего путнего на ней сделать не сможет. Всё равно придётся долго читать документацию и думать.

А у господина автора посыл «если б не надо было учить все эти не-SQL языки, я бы был огого-админом любой СУБД».
Вот раньше-то трава была зеленее и вода мокрее
Не к автору статьи: я вас прошу не путать Active Record ORM с ORM близкими к SQL. К первым относятся практически все новомодные свистелки типа EF, Hibernate, Django, etc. Ко вторым отношу linq2db, и, возможно SqlAlchemy (извините не особо питонирую). Если вам не хватает гибкости — вперед есть альтернативы.
И что примечательно, что active record — это то что принимается легче чем SQL. Естственно, на них легче накрутить патерны репозиторий, UoW, и вообще забыть что такое база данных и как ее готовят. И тут всплакнули алгоритмы оптимиций баз данных, их просто обошли сторной,. Да и зачем, есть id, мы по ней прыгаем, по них удаляем, джоины практически на клиентской стороне делаем. А кеши! Вот она панацея от неугодного «кверяния».
Да не обижу знатоков EF, знающих как обходить его ухабы, просто советую не зацикливайтесь.
Да, точно, не использует. Я уже и не вспомню какой у них для этого патерн. EntityServices?
Не уверен что этот паттерн имеет какое-то специальное название.

https://msdn.microsoft.com/en-us/library/system.data.entity.dbcontext(v=vs.113).aspx


A DbContext instance represents a combination of the Unit Of Work and Repository patterns such that it can be used to query from a database and group together changes that will then be written back to the store as a unit.
Опять же, такая инкапсуляция — не всегда плохо. Есть куча приложений, где просто достаточно правильно спроектировать БД и правильно расставить индексы — и да, после этого можно разрабатывать бизнес-логику даже не зная что за база там крутится, MySQL или PostgreSQL или ещё что-то. Да, возможно запросы не везде будут оптимальны, возможно где-то будет работать медленнее, но это не всегда критично. Зато программист теперь сможет писать чисто бизнес-логику, не задумываясь о бойлерплейте. И становится гораздо легче подключать падаванов к работе, так как есть чёткий и понятный API.

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

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

Зато программист теперь сможет писать чисто бизнес-логику, не задумываясь о бойлерплейте. И становится гораздо легче подключать падаванов к работе, так как есть чёткий и понятный API.
Ну так разнесите код по уровням, DAO и Service.
Так ORM именно это и делает из коробки.
Разнес и просел перформанс. Я в таких случаях всегда рекомендую не репозиторить без надобности, а попробовать писать прямо через ORM, насоздавав необходимых хелперов.

Я противник патерна репозиторий и уж тем более generic repository, так как считаю что вместо улучшения кода они порождают тучу затычек да и время разработки увеличивают раза в два.

Хотите тестировать, тестируйте на эталонной базе функциональными тестами. Да это медленней, но это чертовски как надежней. И констрейнты срабатывают и хранимки дергаются.
Поддерживаю автора.
Поделюсь болью…
Работаю с СУБД с 1992, разные прослойки между базками и приложениями появлялись и забывались, а SQL продолжает спасать производительность, когда оптимизация на уровне ORM бессильна.
Последнюю боль, NHiberbate, 10 лет назад внешние консультанты втолкнули в архитектуру.
Толпы братских народов налопатили перлы и получили монстра. SQL Profiler давно уже забросили как бесполезный — ORM генеримые запросы бесполезно трейсить в профайлере.
Переход на облако подвис т.к. ORM генерит немерянное кол-во запросов и если в дата центре этот траффик не заметен, то с переходом на облако общение между application layer и базкой серьезно подтормаживает и выхода не видно, кроме как переписывать наиболее тормознутые куски с ORM на оптимизированные SQL запросы.
Толпы братских народов налопатили перлы и получили монстра.
А, виноват то хибер конечно, а не те самые толпы братских народов.

ПС: вполне живу с хибером в облаке, просто его тоже надо писать оптимально, как и обычный SQL.
SQL Profiler давно уже забросили как бесполезный — ORM генеримые запросы бесполезно трейсить в профайлере.

Ха-ха-ха, очень смешно… Правда, с NHiberbate я не работал — но запросы тех же EF и linq2sql отлично трейсятся в профайлере.


А чтобы не было "огромного числа запросов" — надо отключать ленивую загрузку данных после выхода проекта из стадии прототипа.

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

Нет. Вот так выглядит ленивая загрузка:


var list = db.Foos.Where(a => a.Id = id).ToList();
foreach (var foo in list) 
{
    Console.WriteLine(foo.Bar.Baz.Message); // +2 запроса при каждом выполнении строки
}

А вот так — без:


var list = db.Foos.Where(a => a.Id = id).Include(a => a.Bar.Baz).ToList();
foreach (var foo in list) 
{
    Console.WriteLine(foo.Bar.Baz.Message);
}
С Hibernate тот-же :)
И как Hibernate заранее понимает какие записи ему загружать в нагрузку к основным?

UPD: сам нашел. Судя по документации, в Hibernate нельзя полностью запретить ленивую загрузку — отсюда и возможность кучи запросов. В EF все немного не так — там при отключенной ленивой загрузке первый кусок кода просто упадет с NullReferenceException. Мне кажется, этот вариант более устойчив к "толпам братских народов".

И потому как раз в ЕФ эти братские народы после каждого linq выражения сразу делают ToList, даже если следующей строкой хотят добавить Where или First
Вот наинклюдят, так что пол базы затягивается ;) Шучу конечно. Но я такого насмотрелся за свое время, что жуть берет. Eager loading еще та палка о двух концах. А человеку всего-то надо было сообщения почитать, а оно толпу ентитей протянуло и закешировало.

Вот так без, и черт, я очень надеюсь тут не надо писать AsNoTracking()
var query = db.Foos
   .Where(a => a.Id = id)
   .Select(a => a.Bar.Baz.Message);

foreach (var m in query) 
{
    Console.WriteLine(m);
}
Да, конкретно в этом случае так будет лучше. (И нет, писать AsNoTracking для последовательности строк не имеет смысла)

Но это был просто пример. В реальном коде в цикле будет далеко не один оператор :-)
У вас никогда SQL Server не заканечивал транзакцию по таймауту из-за того что не успевал скомпилировать SQL запросс, который сгенерировал EF (порядка нескольких десятков мегабайт SQL кода)? Мне вот доводилось такое исправлять за умельцами.
Ну… такие умельцы и голый SQL могут на десяток мегабайт написать, хотя это, конечно же, сложнее :-)
Каждый SaaS-продукт должен предоставлять возможность скопировать все данные в мою собственную SQL БД


Как быть с тем, что подобные продукты подразумевают огромные объемы данных.
И процесс копирования может быть даже медленее, чем процесс порождения новых данных в исходной БД?
Ну да, требование интересное, но спорное. А монговцы тутже затребуют выгрузки в документом формате.
Как быть с тем, что подобные продукты подразумевают огромные объемы данных.
Ну так предоставлять, не означает что только так надо делать, а только тем кому нужно.
Да, неприятные ощущения, я не так давно тоже переводил статью довольно крупную и перед самым нажатием «Опубликовать» решил проверить не появилась ли она уже, и конечно же появилась, всего на несколько часов раньше )
Кстати, а что скажете о качестве перевода? А то мне тут пишут, что все очень плохо, например что rant неприлично переводить как «напыщенная речь».
Эта статья — небольшой крик души автора, то что называется накипело. Поэтому когда я переводил эту статью, то в первую очередь хотел передать эмоциональное состояние автора. Ваш перевод мне понравился больше моего.
соглашусь с автором.
да, SQL — неидеален, но действительно понятен.
и раз выучил и всегда используешь.
а запомнить 100500 DSL у разных ORM — довольно напряжно, притом с сомнительной выгодой.
есть, конечно, приятные исключения вроде «абстрактного» SQL, который конвертится уже в специфичный для конкретной БД (вот у hibernate например).
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Изменить настройки темы

Истории