ORM: почему эта задача не имеет решения, но делать с этим, тем не менее, что-то нужно



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

    Причин, наверно, как всегда оно бывает, много. Наверно все они так или иначе достойны рассмотрения, но здесь и сейчас мы поговорим о задаче объектно-реляционного отображения (Object-Relational Mapping, ORM), которая всегда в каком-либо виде стоит за всеми этими «кнопочками и галочками».

    Что мы знаем про ORM


    1. Хранение данных в реляционных таблицах — не сказать, что совсем простое дело, но в целом и идея, и пути её применения достаточно понятны и хорошо исследованы.
    2. С объектным программированием не всё так хорошо (есть несколько конкурирующих подходов), но в целом с реализацией и применением технологий тоже всё более-менее понятно.
    3. И то, и другое — про данные, их хранение и обработку. То есть, по сути, про одно и то же.
    4. Нам кажется логичным, что должен существовать простой, понятный, удобный, предсказуемый и универсальный мостик между этими двумя мирами.
    5. И мы каждый раз этот мостик легко находим, но вот незадача, его простота, понятность, удобство, предсказуемость и универсальность не работают за пределами простых примеров из туториалов.
    6. Страдают все: и разработчики, которым приходится делать массу чрезвычайно скучной работы, и пользователи, которым приходится сражаться с корявым софтом, и бизнес, реализация потребностей которого внезапно оказывается невыносимо долгой и дорогой, и индустрия в целом.
    7. Я видел много разных ORM, но не видел ни одного хорошего. То есть такого, который за пределами простых примеров не превращается в обузу и прокрустово ложе.

    Почему всё так причудливо


    Идейная основа теории и практики реляционных баз данных — исчисление предикатов, то есть раздел математики. Что касается ООП, то аналогичная по прочности идейная основа там отсутствует. Базовую идею ООП можно попытаться сформулировать примерно так: поскольку мир состоит из объектов, то его, этот мир, было бы удобно моделировать созданием объектов внутри программной системы. В этом понимании сразу две ошибки. Во-первых, мир сам по себе не состоит и никогда не состоял из объектов. Во-вторых, извините, но зачем программа обязательно должна моделировать мир? То есть мы имеем концептуально неверное утверждение, прекрасно дополняемое бессмысленной постановкой задачи.

    Всякий ORM – попытка чётко протянуть унифицированное соответствие между, по сути, разделом математики и рыхлым набором разнообразных практик, основанном на соображениях удобства, исторически сложившихся подходах, а также зачастую на легендах, мнениях авторитетов, да и просто заблуждениях. In vitro это можно заставить работать, но in vivo оно обречено выглядеть жалко и приносить больше горя, чем радости.

    О неизбежности объектной ориентированности


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

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

    Что мы храним в своих базах данных


    Как говорилось выше, реляционные базы данных основываются на исчислении предикатов. Предикат — это сформулированный и в нашем случае сохранённый на носитель факт. На всякий случай замечу, что реляционность базы данных — это не только и не столько про связи между таблицами по внешним ключам. В правильной терминологии отношениями (relations) называется то, что мы по-простому называем таблицами. То есть даже если в вашей базе всего одна таблица с двумя колонками (например, имя и номер телефона), у вас уже реляционная база, устанавливающая отношение между двумя множествами, в данном случае множествами имён и номеров телефонов. Реляционная база не хранит объекты, она хранит факты. Хранимые факты, конечно же, имеют предмет («о чём этот факт?»), и когда мы пытаемся научить систему отвечать на этот вопрос, у нас появляются сущности, то есть объекты, с которыми связаны факты. Если работать правильно, структура базы у нас рождается из серии ответов на вопрос «какого рода факты мы намерены сохранять?», и лишь на следующем этапе у нас появляется нечто, напоминающее объекты, придающие фактам предметность. Можно, конечно, проектировать и «от объектов», но я бы не рекомендовал так делать иначе как на лабораторных работах по предметам, не связанным напрямую с проектированием баз данных. Слишком велика опасность тяжёлых архитектурных просчётов. Кроме того, это как минимум неудобно.

    Небольшое лирическое отступление про объектные базы данных. Очень простая мысль: если мы устали от проблем с ORM, то, может быть, нам сделать что-нибудь с той его частью, которая «R»? Пусть наша база данных будет не жёстким и требовательным реляционным чудовищем, а чем-нибудь модным молодёжным, специально заточенным на хранение объектов. Какая-нибудь бессхемная NoSQL-база, например. Но в конечном итоге внезапно оказывается, что NoSQL-ные ORM ещё кривее и противоестественнее, чем старые добрые SQL-ные. Да и вообще, иметь и с удовольствием эксплуатировать бессхемную СУБД мы можем, но бессхемных данных в природе не бывает. Нет ничего более беспомощного, безответственного и безнравственного, чем ORM для бессхемных баз данных.

    Хороший ORM


    Хороший ORM – отсутствующий ORM. Ну серьёзно. Посмотрите на любую из своих использующих ORM систем и честно попытайтесь ответить себе на вопрос: какие профиты приносит этот монстр? Чем обусловлено его применение кроме как несбывшимися обещаниями счастья и многократно дискредитировавшими себя предрассудками? Безусловно, найдутся некоторые полезные удобняшки, но что они на фоне привносимых архитектурных деформаций и постоянно возникающих на ровном месте проблем с производительностью?

    Как правило, «низкоуровневый» API базы данных прост, удобен, полн и консистентен. Обычно хватает пальцев рук для того, чтобы перечислить все функции. Выучить их — не бог весть какая задача.

    Попробую набросать набор архитектурных принципов, позволяющих замэппить объекты на базу данных без использования ORM:

    1. Храним факты, оперируем объектами. Помним, что база данных хранит факты, а объектные модели, задействованные в обработке данных — это проекции наборов фактов с разных точек зрения. Например, для приведённого примера с именами и номерами телефонов у нас может быть класс Abonent, для которого может быть сохранено несколько номеров, а также класс PhoneNumber, для которого может быть задано несколько абонентов (не забываем, что кроме персональных мобильных номеров у нас кое-где ещё остались квартирные и офисные телефоны). А таблица в базе — всего одна, задающая отношение многие-ко-многим между множеством имён и номеров телефонов. Просто две разные проекции. Этот подход, кстати, нормально масштабируется на гораздо более сложные случаи, позволяя вам иметь в системе такие полезные классы, как, например, «средний объём продаж за заданный период по заданной комбинации критериев».
    2. Факты проецируются в объекты и обратно через проблемно-ориентированный API. Без применения решения, претендующего на универсальность. Если вдруг ещё не умеете сочинять удобные API, так научитесь. И что тоже немаловажно, приучите себя сразу же их документировать.
    3. Порядок превыше всего. Если вы используете классический вариант СУБД с жёсткой схемой данных, то эта схема уже сама по себе вносит порядок в работу с данными. Дополнительная структура, закодированная структурой объектов, просто избыточна. Если вы используете бессхемную СУБД, то вам, конечно, придётся приложить некоторые усилия к тому, чтобы ваша база данных не превратилась в гору мусора вследствие того, что разные разработчики имеют разное представление о том, что где хранится.
    4. СУБД-независимость (если нужно). Если наиболее интересным свойством ORM для вас является СУБД-независимость, то используйте такие спецсредства, как ODBC, JDBC, ADO, или если это невозможно, изготовьте собственный уровень абстракции. Это не так сложно, как сначала может показаться. Вам же для вашей задачи не нужна поддержка абсолютно всех возможностей абсолютно всех СУБД, правда ведь?
    5. Не забываем про дополнительные аспекты работы с данными. Такие, как, например, разделение доступа к данным (которое может быть сколь угодно сложным), мониторинг, репликации и прочее. Но здесь у меня для вас хорошие новости: поскольку в вашем любимом ORM нужный вам доп. аспект всё равно реализован по принципу «всем не угодишь, ешьте что дают», отказ от сомнительного сервиса, с которым приходится больше бороться, чем сотрудничать, в конечном счёте окажется стратегически правильным решением.

    Итого


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

    P.S. Я искренне извиняюсь перед теми братьями по профессии, кто вложил массу времени и усилий в создание многочисленных универсальных лучших в мире ORM. Мне правда очень жаль.
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

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

      0
      Суть проблемы в том, что ООП не обладает замкнутостью, а реляционные структуры обладают.
        0
        В принципе, все технологии программирования, включая ОППшные, замкнуты друг на друга через Тьюринг-полноту. Впрочем, не суть, и Вы, наверно, не об этом.
          0

          Верно. И с SQL и NoSQL используют теорию множеств и конечные автоматы для обработки. Причем в случае с NoSQL некоторое начальное подмножество данных уже обладает собственным контекстом. В SQL он вычисляется/cохраняется в процессе обработки. Может ли NoSQL иметь реляционные признаки? Yes.

            0
            Совершенно верно. Когда я, например, разбираюсь с тем, что там как разложено в NoSQL-базе, всё равно рисую старую добрую ER-диаграмму. Народ сначала смотрит дико, а потом втыкает, что это действительно удобно и наглядно.
              0
              Остался один шаг — строите объекты по ER-диаграмме и задача ORM становится тривиальной.
                0

                Зачем ER? Делаете классы и связи между ними и вот вам ER. А по ней автоматом генерируется схема, если она вообще нужна вашей СУБД.
                Разве не так все работают?

                  0
                  Как построить объекты для приведённого издевательского примера про имена и номера телефонов? Объект «запись в книжке» в целом бесполезен. Полезные объекты — Абонент (у которого может быть несколько номеров) и Номер (который может использоваться несколькими абонентами). Ни одна ORM не потянет мэппинг двух классов на одну таблицу.
                  Вы скажете, что это извращение. Возможно, но в дизайне баз данных ещё и не такие извращения иногда доводится крутить, при этом, что характерно, оставаясь в рамках высочайших стандартов нормализации данных.
                    +1
                    Ни одна ORM не потянет мэппинг двух классов на одну таблицу.

                    Вполне потянет в общем случае. Конкретный кейс, когда, кажется, таблица состоит исключительно из primary key составного — наверное, нет. Но с суррогатным идентификатором потянет объект типа "Запись книжки" с объектными (ValueObject) свойствами "Абонент" и "Номер".

                      0
                      Ну это же халтура! Никому не нужен объект «Запись книжки». Когда ищу по имени, мне нужен список объектов «Абоненты», а когда приходит входящий звонок, отображается карточка объекта «Телефонный номер». Объект «Запись книжки» — лишняя сущность. Да ещё и снабжённая суррогатным ключом.

                      Прочувствуйте пожалуйста красоту идеи ситуационно-зависимого проецирования набора фактов на объекты, с которыми работает логика и пользователи. С одной стороны зашли — один набор объектов, с другой стороны зашли — другой набор, с третьей — третий. На одних и тех же фактах. Данные в базе хранятся естественным для них образом. Объекты выстраиваются опять же естественным для них, объектов, образом. То, что одно другому один-в-один не соответствует — да и не очень-то хотелось. ORM требует, чтобы соответствовало, и в результате у нас или база кривая, или миллион строк невменяемого кода, или и то, и другое вместе. Ребята, зачем?
                        0
                        ORM требует, чтобы соответствовало,

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

                          0
                          Последнее. Объекты мэппятся на отношения. Вы создаёте/правите структуру персистентных классов, и шайтан-фреймворк сам допиливает метаданные БД. Если сказать ему, что классы «Абонент» и «Номер» нужно отмэппить на одно и то же отношение, он решит, что разраб сошёл с ума и откажется так делать.
                            +1
                            шайтан-фреймворк сам допиливает метаданные БД.

                            Насколько мне известно, это далеко не всегда так


                            что классы «Абонент» и «Номер» нужно отмэппить на одно и то же отношение, он решит, что разраб сошёл с ума и откажется так

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

                              0

                              Да не откажется в нормальной ORM. Может в лоб не получится, но получить классы «Абонент» и «Номер» на базе одной таблицы вполне возможно.

                            0
                            Еще не нравится то, что ORM, как правило (наверное, не все), требуют определенной структуры таблиц в БД (например обязательное наличие primary key из единственного столбца), так что уже существующую БД использовать с ORM бывает проблематично. Так вижу. Ну и по QueryBuilder'ам — код сложных запросов в результате получается гораздо менее читабельным, чем просто SQL, при том что сам SQL (может и не синтаксис, но сам принцип) знать таки всё равно надо.
                              +1

                              Не все 100%


                              Некоторые ORM имеют свой язык запросов, *QL и запросы на них писать и читать легче, имея в виду получение объектов, типа SELECT User FROM user WHERE User.name = 'Вася'

                              0

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


                              А так вам никто не мешает результаты запроса типа SELECT abonent FROM phone_abonent WHERE abonent = 'Вася' маппить на VO "Абонент" без инстанцирования объектов «Запись книжки». А вот если захотите выполнить INSERT средствами ORM, то будьте добры инстанцировать как в SQL вы задаёте кортеж.


                              P.S. Без суррогатного ключа можно обойтись, PK не обязан быть примитивного типа.

                                –1
                                Почему лишняя, если в реальности она «физически» существует в виде кортежа реляции?
                                Физически мы все состоим из атомов, но функционируем как единое целое. Аналогия понятна?
                                  +1

                                  Вот именно поэтому класс «Запись книжки» нужен, что это одно целое :) А без него это как раз будет попыткой описать человека как множество несвязанных атомов :)

                                0
                                Как построить объекты для приведённого издевательского примера про имена и номера телефонов? Полезные объекты — Абонент (у которого может быть несколько номеров) и Номер (который может использоваться несколькими абонентами).

                                Так и делаете. Класс "Абонент" (id, name, ...) и класс "Номер" (number, ...), первое поле — первичный ключ. Между ними связь "Абонент — Номер". И такие же таблицы в базе. У вас "таблица в базе одна" потому что вы объединили их в одну, нарушив правильную модель. Представьте, что кроме имени у абонента еще и адрес есть, или номер офиса. Что у вас будет в одной таблице? Правильно, куча дублирующейся информации и возможность ее несоответствия в разных дублях. Из-за этого и появляются всякие костыли в коде типа "Нужно только исключить ситуацию, когда пользователь под именем «Маша» записывает десять разных Маш. Решается на уровне пользовательского интерфейса". При правильной модели этих проверок вообще нет.


                                Объекты "Запись книжки" в коде это массив указателей на объекты "Абонент" в объекте класса "Телефон" или "Телефон" в объекте класса "Абонент". Вы в принципе не сможете без них обойтись, даже если будете делать без БД.

                              0
                              Ваш пример — это культурный феномен. Как мраморный телефон у Хоттабыча. Он прекрасен, только если по нему не звонить. А если звонить — то возникает объект Вызов и не один, а Входящий и Исходящий. Они то однозначно и связывают Абонента и Номер.
                        –3

                        Компьютер не является Тьюринг-полным, у него память ограничена. Даже в отрыве от конкретной железки, например, в C/C++ указатель — тип фиксированного размера (не важно что стандарт языка не специфицирует этот размер, важно что он конечен), поэтому они принципиально неспособны адресовать бесконечную память.


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

                      +2

                      Вы всё же недооцениваете Объектные СУБД. Почитайте, хотя бы, это: https://habr.com/ru/post/267079/

                        0
                        -7 у статьи какбэ намекает

                        Даже проще: буде носклы сколь конкурентоспособны в нише субд_для_делания_денег уже давно ораклы и майкрософты имели/купили бы подобный продукт. Суть в том, что как только речь идет не про удобно, а про производительность и всякий хайлоад, то носкл базам предъявить особо нечего… собсно поэтому то и мы возвращаемся к п.1 — для поиграться майкрософты внедрили элементы носкл и этом успокоились.
                          +1
                          Nosql это как бы «все остальное» и так одним махом на все поставить печать не совсем аргументированно. Например redis уже сегодня занял прочное место в приложениях от мала до велика. Если про производительность то cassandra имея в виду быстроту добавления новых записей плюс распределённая обработка данных очень даже то что нужно. Хотя это как и redis не замена реляционным базам. Есть конечно класс баз который близко пересекается с рсубд это документо ориентированные и я поддерживая несколько проектов на монге могу сказать что профита ноль в частности и по скорости а проблем масса. Хотя например couchdb если иметь в виду слова разработчика что couchdb плох во вcем кроме репликации для какого то класса задач подойдёт.

                          Ну а про графориентированные или как их раньше называли сетевые то это даже в учебниках 70х годов писали что это наиболее мощные базы. Просто не было их реализаций доступных какое то время. Сейчас появились по крайней мере три orientdb, arangodb, neo4j. Последняя имеет сложное лицензирование и скорее всего для многих именно поэтому не подойдёт. Вторая из них отлично работает но только пока данные помещаются в оперативную память. А вот orientdb очень даже удобная штука. Конечно ничего не могу сказать о ее надёжности. Опыта в этом смысле нет.
                            0
                            Вообще то СУРБД реализуют РМД. Как у любой модели у неё есть границы применимости. Например графы и деревья в РМД не очень удобно хранить.
                            Поэтому NoSql вполне находят свою нишу. Но она очень специфична. Поэтому крупные сенсоры «не заморачиваются», но вводят костыли типа функций для работы с json и xml.
                            Для подавляющего типа задач этого достаточно. А если нет… То есть свободные NoSql. А на бесплатном трудно заработать.
                              +3
                              -7 у статьи какбэ намекает

                              На что же намекает?


                              уже давно ораклы и майкрософты имели/купили бы подобный продукт

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

                              +4
                              Спасибо за ссылку. Хорошая статья, и заминусовали её, скорее всего, за слишком провокационный заголовок.
                                0
                                У vintage большой антирейтинг вовсе из-за не заголовков, а потому что сомневается в «священных коровах».
                                  +2

                                  Как по мне, то не из-за сомнений, а из-за отсутствия сомнений, безапеляционном способе подачи своей точки зрения. Хотя в последнее время, кажется, вместо " не нужен", чаще встречается "зачем нужен , если можно...?" :)

                                    0

                                    Какой мой тезис не был подкреплён аргументами?

                                      +1

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

                                        0

                                        Между "вы не предъявляете аргументов" и "мне не нравятся ваши аргументы" — целая пропасть. Первое — легко проверяемый факт. Во втором же случае — поди разберись кто из нас двоих дурак.


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

                                          0

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

                                            0

                                            То есть, если написал "как по мне", то можно не отвечать за свои слова, а если не написал — то это безапелляционное заявление. Так это работает?

                                              0

                                              Что значит "не отвечать"?

                              +4
                              Честно говоря во всей связке технологий именно с orm никогда не испытывал проблем описанных автором. В некоторых нехвстает удобства миграции и переводимых или локализованных полей. Профит который имеем от orm это исключение инъекций, валидации данных и вычислимые поля. Далее удобно получить объект в свёрнутом виде при отношении один к многим.

                              Неудобства разработки они скорее лежат в плоскости restapiесли оно используется, фронтенда. А orm скорее стабилизирует разработку.

                              Хотя могу согласиться что когда начинается разработка структуры с выделением сущностей а не по простому нарисую как структура базы. То часто получаются химеры.
                                +3
                                Проблемы есть, это производительность. То что вы их не испытали, скорее говорит о не критичном объёме данных.
                                Очень часто модель строят по тому как ему удобно, а не так как эффективно движку базы. Потом начинаются рефакторинги сего добра, и дай бог чтобы миграции не загнулись.
                                  0

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

                                    0
                                    Ну это дело я могу предсказать для конкретной ORM.
                                    Проблема в том что определенные подходы работы ORM требуют дополнительных телодвижений с базой.
                                    UPDATE — перед тем как изменить рекорд его надо загрузить из базы потом апдейтгуть.
                                    DELETE — те же грабли
                                    INSERT — получим дополнительный селект после вставки, или если умная ORM, то сделает INSERT WITH OUTPUT. Сказать чтобы вообще ничего не тянуло нельзя.

                                    Можно продолжать список
                                      0
                                      UPDATE — перед тем как изменить рекорд его надо загрузить из базы потом апдейтгуть

                                      Не во всех ORM это обязательно. Проблемы тут возникают только на массовых операциях.


                                      Сказать чтобы вообще ничего не тянуло нельзя.

                                      А зачем?

                                        0
                                        Fire and forget. Лишние операции — больше нагрузка на базу и сеть. Ты еще удивишься как иногда замена параметра на литерал внезапно ускоряет запрос. Всему нужен тюнинг. Если этого нет, тот кто оптимизирует за вами, просто переколбашивает это на SQL.
                                      0
                                      не у всех.
                                      такое мощные, как hibernate, sqlalchemy и т.д. вполне себе предсказуемы и гибки.
                                        0
                                        Тут буквально на днях была занятная статья в которой было описано весьма, скажем так, неочевидное поведение hibernate :)
                                          0
                                          но там же можно любое задать.
                                      +1
                                      В случае ORM чтобы не испытывать боли с производительностью не стройте схему БД от объектной модели. Работает только наоборот. При этом можно словить некоторую боль в ORM, но это заметно лучше чем словить боль от натянутой на БД объектной модели.
                                        +1

                                        А что не так со схемой БД, построенной по объектной модели? Можете привести примеры боли с производительностью?

                                          +1
                                          То что она может не учитывать особенности реляционной модели, от слова совсем. В итоге можно легко получить БД которая даже первой нормальной форме не соответствует. К примеру люди добавляют в БД человека и вкладывают в него данные о документах или данные о том где он живет. Кладут в объект не думая о СУБД вообще в итоге данные просто лежат общим куском. Дополнительно очень часто такой подход рождает не оптимальные запросы к БД так называемая проблема N+1 когда ORM генерирует 100500 запросов вместо одного запроса с добавлением JOIN.

                                            0
                                            В итоге можно легко получить БД которая даже первой нормальной форме не соответствует. К примеру люди добавляют в БД человека и вкладывают в него данные о документах или данные о том где он живет.

                                            А где здесь нарушение первой нормальной формы? ORM либо "раскроет" вложенные объекты, либо создаст связанные таблицы. В обоих случаях первая нормальная форма будет соблюдена… благодаря ORM.


                                            Дополнительно очень часто такой подход рождает не оптимальные запросы к БД так называемая проблема N+1...

                                            … решаемая в EF уж тремя разными способами (проекции, eager loading и explicit loading)

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

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

                                              … решаемая в EF уж тремя разными способами (проекции, eager loading и explicit loading)

                                              Которые явно надо указывать. По умолчанию будет именно N+1. В итоге опять скатываемся на уровень БД.

                                              Как бы я говорю о тех кто использует ORM в режиме «я ничего не хочу знать о БД пусть ORM за меня сделает». Это обычно приводит к плачевным результатам. Когда же люди помнят о наличии БД, то заметно проще сначала сделать бд, а потом отобразить ее в объекты.

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

                                                EF — да. Любой класс, не помеченный явно как составной тип, считается отдельной сущностью, а свойства этого типа — связями 0..1 ко многим.


                                                Которые явно надо указывать. По умолчанию будет именно N+1.

                                                Неа, по умолчанию будет null (при выключенной ленивой загрузке)

                                                  0
                                                  Не EF единым. Во других ORM это зависит от реализации в JPA к примеру нет. Делайте правильно сразу.

                                                  А ленивая загрузка по умолчанию выключена? Что-то мне подсказывает что нет. Чаще всего как раз нет.
                                                    0
                                                    А ленивая загрузка по умолчанию выключена?

                                                    В EF 6 — нет (но не вижу проблем её взять и выключить), в EF Core её просто нет.


                                                    Кстати, ленивая загрузка недоступна если свойство не виртуальное, чего "по умолчанию" не делают.

                                                      0
                                                      Как бы у вас использование ORM с пониманием что за ним москва БД. А люди часто про это не думают. Это же ORM она пусть за нас думает. Потом рожают крокодилов, которые генерят такие запросы, что DBA при изучении начинает пить валидол.
                                                        0
                                                        Потом рожают крокодилов, которые генерят такие запросы, что DBA при изучении начинает пить валидол.

                                                        А зачем DBA изучать эти запросы?..


                                                        Раз уж дошло дело до подключения DBA — надо выкидывать запрос, и писать хранимку.

                                                          0
                                                          Внезапно чтобы произвести оптимизацию работу приложения. К нему приходят говорят у нас что-то приложение работает плохо. Он такой окей открывает монитор, а там…

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

                                                            Так зачем изучать этих крокодилов-то? Можно и без изучения начать применять те рекомендации. А если не помогло — то начать писать хранимку, опять-таки без изучения крокодилов.

                                                              0
                                                              Мдя. Кажется вам повезло и такое в практике своей не видели. Я вот видел много.

                                                              И да как вы будете хранимку уже в существующее приложение подпихивать и как это поможет решить проблему?
                                                                0
                                                                И да как вы будете хранимку уже в существующее приложение подпихивать

                                                                Изменением исходного кода, конечно же. Точно так же этого требуют любые рекомендации по ускорению ORM.


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


                                                                и как это поможет решить проблему?

                                                                В хранимке DBA сможет написать запрос в любом виде, используя любые техники оптимизации которые ему подсказывают опыт и explain.

                                                                  0
                                                                  Как бы заметно проще сказать посоны вот тут добавьте пару хинтов в код. И все станет хорошо. И ничего ему самому писать не надо будет.
                                                                    +1

                                                                    Если мы рассматриваем ситуацию "куча человек без знания SQL + 1 DBA" — то писать запрос в любом случае именно ему и придется.


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

                                                                      –1
                                                                      В этом случае как раз хорошо педалит нормально написанный ORM. Один человек делается для кучи человек готовый интерфейс. Они радостно используют свое объектное, а он пишет нормальную БД. И в этом случае как раз и будет происходить сначала схема БД, а уже потом объекты.
                                                              0
                                                              Тут EF Core работает как надо, жадная загрузка это их все, хотя опять же не все тут гладко.

                                                              Проблема в запросах которые пишут хипстеры без знания SQL.
                                                                0
                                                                Ну с жадной загрузкой можно переборщить и тянуть к себе полбазы гг
                                                            0
                                                            Истина, они такое на linq городят что бедные EF-щики рыдают. Да и в принципе разрулить идеально не могут.
                                                            Наинклюдают неглядя с пол базы, хотя надо конкретно небольшие части трех таблиц.
                                            +2
                                            Если данные имеют мега-критический объем это уже другая история используются другие средства в частности и другие базы данных
                                            Если ORM генерирует предсказуемые операторы SQL то не могу представить как те же самые операторы SQL выполненные из ORM будут выполняться медленнее. Хорошие ORM например вычисляют разницу и генерируеют только реально изменившиеся поля в запросах а также формируют в одном запросе несколько опрераторов т.к. в данном случае операторы обновления выполняются по ленивой схеме — сначала накапливаются в буфер а потом сохраняются в базу.

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

                                              Что касается химер, чисто ИМХО, они появляются в тот момент, когда некие сущности из-за особенности языка опускаются из описания. Например, тот же указатель в C++ — это «связь 1-1_либо_много», точный тип которой нельзя получить из контекста, а сам указатель не имеет собственного объекта и\или состояния. Поэтому реализовать в C++ ORM средствами лишь самого языка нельзя, а любые попытки впихнуть компромиссный вариант натыкаются на боль и унижение (на выбор, тонны макросов или работа только с небольшим подмножеством языка). Единственное решение — избавиться от сырых указателей и явно прописывать этот самый объект. А оно не самое тривиальное.
                                                0
                                                В смысле стандартов C и C++, кстати, указатель как раз объект — хранящаяся по какому-то адресу значение (адрес чего-то другого). Так что учитывая свойственную дискуссиям на хабре небрежность в обращении с терминологией…
                                                Как говаривал Козьма Прутков (по памяти): «Многие вещи недоступны нашим понятиям не оттого, что понятия наши слАбы, а оттого, что вещи сии не входят в круг наших понятий.».
                                                Простите, навеяло…
                                                  0
                                                  В смысле стандартов C и C++, кстати, указатель как раз объект — хранящаяся по какому-то адресу значение (адрес чего-то другого).
                                                  Если выражаться терминами стандарта C++… Но ведь это не верно по сути, поскольку указатель, как и многие другие типы в C++, обладает бо́льшим числом свойств, чем хранит фактически. Ну, например, настоящий тип, валидность объекта по адресу, факт владения, факт разделения владения, число указателей на объект и тп. Они являются свойствами указателя, свойствами адреса, но!.. Адрес — не объект, наоборот, у объекта есть свойство «адрес». А указателям нужно подключать std::shared_ptr\std::weak_ptr для того, чтобы эти свойства явно иметь. И даже так, многие проблемы остаются нерешёнными.

                                                  P.S. Можно ниже устроить срач на тему «Является ли валидность (и тп) объекта по адресу свойством указателя, свойством адреса или даже самого объекта?». Тема богатая для срачей, но абсолютно бессмысленная по сути, поскольку в самом языке указатель на любой адрес, отличный от nullptr, является валидным. То есть, с точки зрения языка не существует невалидных объектов. Просто некоторые из этих валидных объектов могут быть не очень съедобными для среды выполнения. По стандарту это так, а по сути — мы все знаем, как.
                                                  P.P.S. Да, я знаю, что валидность объектов по адресу гарантируется только при условии получения этого адреса от new или оператора &. Но вам ведь ясно написали «гарантируется». Это не значит, что нельзя его получать из malloc или 100500[myArray]. Или вообще наплевать на это дело и использовать численные значения, сидя в микроконтроллере.
                                          +1
                                          Сначала тоже повелся на моду на ORM, писал и свои поделки. Но чуть что-то более-менее серьезное, сложнее чем получить запись по primary key или сохранить, сразу начинается ковыряние в исходниках ORM-библиотеки, чтобы понять, что же пошло не так (я несколько утрирую, конечно). ИМХО это попытки скрестить ежа с ужом. Понятно, писать SQL-запросы прямо в коде тоже не лучший вариант, но их можно вынести куда-то в конфигурацию, тогда и проблем с переносом на другую СУБД меньше.
                                            0
                                            P.S. Когда писал коммент, то думал одновременно и за ORM, и за ActiveRecord, и за QueryBuilder.
                                              0
                                              Для CRUD и несложных случаев хорошо. Просто надо понимать как и что происходит под капотом. Очень часто есть возможность подсмотреть чего там ORM генерит. Понимая как и какие ключи на это влияют можно подкрутить производительность во многих случаях парой изменений.
                                              0
                                              А таблица в базе — всего одна, задающая отношение многие-ко-многим между множеством имён и номеров телефонов.

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

                                                0
                                                С нормализацией в данном случае, что удивительно, всё в порядке. Старая добрая веками прекрасно зарекомендовавшая себя технология бумажного блокнотика.
                                                Несколько записей по одному и тому же имени абонента — нормальная ситуация. Нужно только исключить ситуацию, когда пользователь под именем «Маша» записывает десять разных Маш. Решается на уровне пользовательского интерфейса.
                                                  0
                                                  Несколько записей по одному и тому же имени абонента — нормальная ситуация.

                                                  Да, но нет. Несколько записей (с разными номерами, предполагаем что номер является ключом, то есть уникален) с одинаковым именем абонента — это информация о номерах… разных абонентов! Если только у вас нет отдельной таблицы, в которой ключом является уже имя и первая таблица не связана с ней по внешнему ключу.
                                                    0
                                                    Эта таблица имеет составной первичный ключ, в который входят обе колонки. Только не говорите пожалуйста, что так не бывает.
                                                      0

                                                      Какого типа колонка "имя абонента"? Есть ли у абонента что-то кроме имени и телефонов? Что происходит если в имени абонента нашли опечатку или его надо переименовать?

                                                        0
                                                        Бывает по всякому. Тем не менее такой вариант будет неполным и весьма корявым (то есть, по сути, будет являться промежуточной таблицей связи в реализации много-много с естественными ключами, но с отсутствующими таблицами первичных сущностей и, соответственно, без внешних ключей). Собственно ApeCoder уже задал правильные вопросы и это только малая часть из них :)
                                                          0
                                                          И да, при ключе из двух полей он (как единственная таблица) будет совершенно бессмысленным. Потому что я не могу представить себе смысл этой сущности (ну, кроме реально пустопорожнего «связь какой-то одной строки (абонент) с какой-то другой строкой (номер)»).
                                                        0
                                                        Не совсем так. Хотя похоже на истину. Я сейчас постараюсь это доказать. С точки зрения чисто формальной все вроде бы нормализовано (если не учитывать логику задачи)

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

                                                        Но что же со случаем когда ровно два поля: телефон и имя. Пусть Маша сменила имя и стала Мария. Во что выливается такое изменение: нам нужно выбрать все строчки Маша и присвоить им имя Мария. Уже есть подозрение что нарушена нормализация Собственно так и есть. Если сформулировать то получается что поле «имя» в таблице зависит от значений поля «имя» этой де таблицы. Честно говоря я не осилил формы дальше 3-й хотя как правило разрабатываю оптимальные структуры данных Но кажется это многозначная зависимость и нарушение 4нормальнйо формы. А так же есть еще 6 нормальная которая говорит о том что прока декомпозиция возможна это еще не 6-я нормальная форма.

                                                        Немного хочу обратить внимание что хотя я и не нашел этого в определениях Кодда и Дейта, но в одном из наших старых учебников по информатике ко всем определениям НФ была добавлена фраза "… с учетом выполняемой задачи". То есть например атомарность это неделимость данных в связи с выполняемой задачей. И т.д. Поэтому чисто формально первичный ключ по двум уникальным полям есть. А вот когда начинаешь разбирать то что связано с задачей то понимаешь что форма хотя и нормальная но в каких-то начальных уровнях.
                                                      +6
                                                      Базовую идею ООП можно попытаться сформулировать примерно так: поскольку мир состоит из объектов, то его, этот мир, было бы удобно моделировать созданием объектов внутри программной системы.

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

                                                      Дальше, впрочем, не лучше.

                                                      Есть отличная статья в Википедии: Object-relational impedance mismatch. Там собрана серьёзная, качественная критика ORM:
                                                      • SQL чужда инкапсуляция,
                                                      • SQL не предполагает той связи между данными и процедурами (существительным и глаголом), которая является характеристической чертой ООП,
                                                      • ООП подразумевает, что объект имеет уникальный идентификатор, а результат запроса по сути своей не может однозначно идентифицироваться,
                                                      • и т. д.

                                                      Но уважаемый maslyaev вместо критики предлагает нам набор соломенных чучелок:
                                                      • ООП − это плохо, поэтому ORM − тоже плохо. Однако ООП широко применяется на практике, поэтому нам придётся с ним считаться. Распространить тот же принцип практической применимости на ORM автор почему-то не хочет,
                                                      • ORM не является проблемно-ориентированным, поэтому не нужен,
                                                      • задачу независимости программы от конкретной СУБД уже решали раньше (на самом деле ODBC весьма хреново решает эту задачу по сравнению со многими ORM, ну да ладно),
                                                      • абстрактный мэппинг − это слишком сложно, мы лучше сделаем много ситуационных мэппингов, типа «средний объём продаж за заданный период», и будем потом весь этот код поддерживать,
                                                      • и т. д.

                                                      Разумеется, список проблем, которые решают (успешно, на практике) ORM у автора далеко не полон. Вот только наиболее очевидные для меня:
                                                      • безопасность запросов (в смысле изоляции от пользовательского ввода),
                                                      • оптимизация обращений к СУБД (каскадное конструирование запроса, отложенное выполнение),
                                                      • миграции!

                                                      Лично мне прочувствовать пользу ORM на практике помогло написание нескольких небольших проектов на Python без ORM. Начав писать свой абстрактный уровень поверх MySQLdb, я понял, что занимаюсь ерундой. Сравнивая Django ORM, SQLAlchemy или PonyORM с тогдашними моими экзерсисами, я понимаю, что ORM − не кошмар и не гиря на ноге, а реальные помощники в работе, пусть и не идеальные.
                                                        0
                                                        Спасибо за интересную ссылочку. Хотя при прочтении этой статьи может возникнуть ложное ощущение, что проблема в том, что технология РБД лет на 40 отстала от прогресса.

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

                                                        безопасность запросов
                                                        Инъекции? Ну, не знаю. Как-то совсем давно не вписывал значения в прикладном коде прямо в SQL-строку, отправляемую на сервер. Что с ORM, что без. Как-то сразу приучил себя предохраняться.

                                                        оптимизация обращений к СУБД (каскадное конструирование запроса, отложенное выполнение)
                                                        Чисто по практике: рефакторинг с отказом от ORM зачастую в разы улучшает производительность именно за счёт более оптимизированной работы с СУБД.

                                                        миграции!
                                                        А что миграции? Вы о чём? Об ETL или об автоматическом апдейте структуры базы?
                                                          +1

                                                          ООП моделирует графы. А графы — тоже раздел математики.

                                                            +1
                                                            Главное чучелко — это то, что невозможно по-настоящему крепко подружить математику и не-математику.
                                                            Что вы имеете в виду под не-математикой? ООП разве не построено на основе теории множеств?

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

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

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

                                                            А что миграции? Вы о чём? Об ETL или об автоматическом апдейте структуры базы?
                                                            Второе.
                                                              –1
                                                              ООП разве не построено на основе теории множеств?
                                                              Нет, не построено. В первом приближении класс — это множество, а экземпляр — это элемент. Но если копнуть глубже, начинаются чудеса. Например, оказывается, что у нас с операциями пересечения и объединения множеств? Вот есть, например, два класса — Customer и Vendor. Как их будем объединять и пересекать? Какие-то странные множества. Теория множеств, в которых определена только операция разбиения на подмножества (наследование). Немножко странно всё это.

                                                              Об ETL или об автоматическом апдейте структуры базы?
                                                              Второе
                                                              Вот я, например, пару недель назад учинил рефакторинг структуры базы (очень хотелось разогнать в пару десятков раз). Написание весьма хитрой миграции у меня заняло пару часов. Почему-то уверен, что через ковыряние во всяких миграционных тонких настройках в духе джанговского каталога «migrations» отняло бы гораздо больше времени, сил и нервов, и всё равно в результате не дало бы той стопроцентной уверенности, которую я получил вручную.
                                                                –1
                                                                Вот есть, например, два класса — Customer и Vendor. Как их будем объединять и пересекать?

                                                                Это называется «множественное наследование».
                                                                class Сounterparty(Customer, Vendor):
                                                                    pass

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

                                                                Писать код не надо:
                                                                $ ./manage.py makemigrations && ./manage.py migrate
                                                                  –1

                                                                  Вообще, контрагент — это покупатель ИЛИ продавец. А у Вас "И".


                                                                  Что касается makemigrations/migrate, то оно по-любому не потянуло бы мою сложную переукладку данных. Всё равно пришлось бы собственно перенос данных рисовать вручную, и делался бы он не групповыми запросами, а пообъектно. То есть в десятки раз дольше. Или сотни.

                                                                    0
                                                                    Вообще, контрагент — это покупатель ИЛИ продавец. А у Вас «И».

                                                                    Ну, с «ИЛИ» вроде нигде проблем нет: это их общий базовый класс. Я думал, вы имеете в виду «И».

                                                                    Что касается makemigrations/migrate, то оно по-любому не потянуло бы мою сложную переукладку данных. Всё равно пришлось бы собственно перенос данных рисовать вручную, и делался бы он не групповыми запросами, а пообъектно. То есть в десятки раз дольше. Или сотни.

                                                                    Если нужно как-то небанально преобразовывать данные, то мы так же делаем `makemigrations` и `migrate`, только между ними мы вписываем в миграцию только тот код, который работает с данными. И не обязательно работать «пообъектно», любые запросы также доступны в этом коде, можно использовать «пакетный» режим (limit/offset) и т. д. И при этом всё правильно оборачивается в транзакции, так что не нужно думать об обработке исключений. Всё это в любом случае упрощает работу и не заставляет идти на компромисы по скорости.
                                                                      –1

                                                                      Вообще то контрагент — это Покупатель И/ ИЛИ Продавец И/ИЛИ Сотрудник. :)

                                                                    +1
                                                                    Вот есть, например, два класса — Customer и Vendor. Как их будем объединять и пересекать

                                                                    В общем, если сделать шаг в сторону от джавового ООП, то вот так (typescript):


                                                                    class Customer {}
                                                                    class Vendor {}
                                                                    
                                                                    type CustomerAndVendor = Customer & Vendor;
                                                                    type CustomerOrVendor = Customer | Vendor;
                                                                      0

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

                                                                        0

                                                                        "Продавец и покупатель" обладает всеми свойствами как продавца, так и покупателя, и может выступать в обоих ролях.


                                                                        Если такое поведение невозможно (конфликт методов или свойств) — ну, значит, такой тип не имеет смысла. Бывает.


                                                                        "Продавец или покупатель" может быть как первым, так и вторым, и обладает методами и свойствами, общими для этих двух типов.


                                                                        Собственно, все как ожидается.

                                                                          0
                                                                          Собственно, все как ожидается.

                                                                          Кому как :) От | я ожидаю что он обладает полными свойствами хотя бы одного из них, что ложный результат проверки на тип Customer гарантирует, что это тип Vendor.

                                                                            0

                                                                            Все так и есть. Без проверки доступны только общие методы, с проверкой — методы того типа, на который проверились. Проверять можно обычным if-ом, а можно написать type guard.
                                                                            В обоих случаях компилятор все понимает, и в теле if-а тип выводит сам.

                                                                              0

                                                                              if понятно, а в else определит?

                                                                                0

                                                                                Определит.

                                                                              0

                                                                              Так и есть. Ну, с поправкой на то, что проверку, по=хорошему, нужно делать структурную ("утиную"), а не номинативную.

                                                                                0

                                                                                Тогда "обладает методами и свойствами, общими для этих двух типов" не совсем корректно, звучит как жёсткое пересечение.

                                                                                  0

                                                                                  По свойствам — да, получается пересечение. А по возможным значениям — именно объединение.

                                                                        0
                                                                        Нет, не построено. В первом приближении класс — это множество, а экземпляр

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

                                                                          +1
                                                                          Миграции в orm как правило есть но не во всех равноценные. Не буду говорить за экосистему дотнет но в опенсорсных на более продвинутые миграции в doctrine php и в typeorm type/javascript. Они сравнивая реальную базу данных со схемой данных автогенерииуют скрипт на языке orm а не на языке sql и после позволяют его редактировать под свои задачи. То есть получаем и Профит в виде программного анализа схемы данных в базе данных и в orm, генерацию миграции но и имеем полный кон роль для того чтобы кастомизировать миграцию
                                                                            0

                                                                            Я тоже хотел привести в пример Doctrine. Из тех кто мне симпатичны можно вспомнить Caminte


                                                                            Объект с точки зрения теории множеств в это кортеж его свойств.

                                                                            Уточню. В кортеже нельзя менять порядок элементов, а в множестве порядок не важен. Если порядок имеет значение использую массив [] если нет, то map {}. Оба два понятия являются объектами и их очень естественно использовать в качестве контейнеров или если рассуждать в терминах шаблонов проектирования, то в качестве медиаторов. Куда можно/нужно пробрасывать/выбрасывать контексты запросов, типов данных или функций. Да классы удобно инстанцировать. Но еще удобнее реплицировать и форкать всю систему сразу. Т.е. самим движком. Естественно с полным контролем извне.

                                                                              0
                                                                              Я не утверждал что кортеж это множество. Я утверждал что если смотреть на объект с точки зрения теории множеств то это кортеж свойств которые каждое является элементом множество. Если мы рассмотрим объект не с точки зрения теоретико-множественного подхода могут быть другие интерпретации
                                                                              Например очень интересная работа есть books.google.com.ua/books/about/%D0%92%D0%B5%D1%89%D0%B8_%D1%81%D0%B2%D0%BE%D0%B9%D1%81%D1%82%D0%B2%D0%B0_%D0%B8_%D0%BE%D1%82%D0%BD%D0%BE%D1%88.html?id=q7EIAwAAQBAJ&redir_esc=y

                                                                              А.И. Уемов Вещи, свойства и отношения

                                                                              Там те же вроде бы понятия но их интерпретация основана на другой базе
                                                                              0

                                                                              doctrine-migrations (кстати, не является частью DoctrineORM) генерирует скрипт миграции на PHP, в котором чистый SQL конкретной версии конкретного диалекта SQL.

                                                                                0
                                                                                Удобство здесь вот в чем
                                                                                1) сравнивается всегда реальная база данных с объектной моделью
                                                                                (другой вариант который мне встречались: в проекте хранится формализованное представление схемы данных kalax(go), sequelize-migration(kavascript)) — профит в том что из любого состояния базы данных можно сделать правильную миграцию. При реализации с сохраненной схемой все сложнее. Если внести неправильные ручные изменения в миграцию или же два разработчика сделают свой вариант миграции от одного коммита, или же в данные будут внесены изменения вручную (не обязательно по структуре данных например можно для ускорения работы создать индекс) то схемы удалятся друг от друга очень быстро и очень далеко
                                                                                2) генерируется скрипт на php. профит — можно добавлять свои дополнительные операторы в зависимости от задачи. например, присвоить значения в добавленную колонку, или заполнить данными новую созданную таблицу если из текущей таблицы была часть столбцы выделена в новую связанную таблицу. Варианты которые я еще встречал: генерируется просто скрипт sql который конечно можно кастомизировать но не получится сделать некоторые действия которые доступны из языка программирования. Еще другой вариант: генерируется условная схема миграции например {table:user, action: add-column: {name: address, type: text}} и т.п. В этом случае понятно что ни о какой кастомизации уже не может быть речи
                                                                                3) это собственно следствие второго: возможность кастомизации. Профит — собственно сама кастомизация. Как противоположный случай можно представить закрытый от доработок метод sync() — который за Вас «все сам делает» но как-то непредсказуемо все это
                                                                                  0

                                                                                  Я лишь указал, что doctrine-migrations не генерирует миграции на каком-то своём языке. Она формирует своё внутренне (PHP runtime) представление как желаемой схемы, так и текущей, и на базе диффа генерирует по сути SQL в PHP обёртке. Декларативный подход к генерации миграций, а не императивный. Про удобство этого подхода я прекрасно знаю :) Про недостатки тоже :(

                                                                              0
                                                                              Не уверен что это теоретикомножественный подход. Вообще-то я склоняюсь к тому что понятия могут иметь различные трактовки например геометрические или теоретикомножественные. Объект с точки зрения теории множеств в это кортеж его свойств. Каждое из свойств как раз и принадлежит некоторому множеству. Кроме того есть методы которые если исключить побочные эффекты переводят объект из одного состояния в другое. Говорить о пересечении объектов насовсем правильно.
                                                                                0

                                                                                По-моему даже с точки зрения теории множеств можно описывать объекты по разному.

                                                                              0
                                                                              ООП разве не построено на основе теории множеств?

                                                                              Чаще вместе с ООП упоминают абстрактные типы данных (ATD), где упор на операциях (поведении, глаголах), связанных со значениями. Реляционная модель фокусируется множествах фактов и логических отношениях между ними. Но на практике ни кто не мешает забивать шурупы микроскопом, использовать RDBMS как хранилище из-за ACID изкаропки, а ООП для тул для реюзинга кода.
                                                                                +1

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

                                                                                  +1

                                                                                  Термины логика и отношения перегружены разными смыслами. Вы кажется говорите про онтоллгии. Здесь же имееются ввиду отношения как в логике счисления предикатов — конъюнкция, дизъюнкция и вот это все. Реляционная алгебра это по сути обобщение ЛСП на множества, что позволяет оперировать не отдельными утверждениями, а сразу пачками подобных (отношениями).


                                                                                  Засада с ORM в том, что когда мы говорим сохранить объект в базу, то имеем ввиду сохранить нечто логичное в терминах ER, но нелогичное в терминах ООП. В ООП объект это в первую очередь поведение, состояние которого напрочь скрыто от внешнего наблюдателя (инкапсулировано). Поведение описывается программой. Но сохраняя объект мы не сохраняем базу поведение, а как раз вот это самое неведомое состояние.


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

                                                                                    +1
                                                                                    Но сохраняя объект мы не сохраняем базу поведение, а как раз вот это самое неведомое состояние.

                                                                                    Обычно мы вообще сохраняем состояние. Далее в ОЗУ состояние лежит отдельно, а проведение — отдельно.

                                                                              0
                                                                              Чисто по практике: рефакторинг с отказом от ORM зачастую в разы улучшает производительность именно за счёт более оптимизированной работы с СУБД.

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

                                                                              0
                                                                              Статья в Вики на которую Вы ссылаетесь также может быть подвергнута критике. Автор статьи в Вики исходит из того что в orm таблица мапптися на бизнес-обьект а на самом деле таблица мапптися на data transfer object. Поэтому у этого объекта есть методы save, delete, точно также как в sql есть операторы insert, update
                                                                                0

                                                                                Что значит "на самом деле"? Ни один из трёх основных паттернов ORM не предполагает маппинг на DTO. Ну и собственно у классического DTO методов save, delete быть не может. Скорее всего вы про паттерн ActiveRecord, а он подразумевает, что объект является бизнес-объектом с дополнительной с возможностью себя сохранять или удалять.

                                                                            • НЛО прилетело и опубликовало эту надпись здесь
                                                                                +5

                                                                                Описанные вами удобства относятся к "Query Builder", который может применятся отдельно от ORM.

                                                                                • НЛО прилетело и опубликовало эту надпись здесь
                                                                                  –5
                                                                                  можно тупо поставить ->where() после ->order_by() и она соберет все как надо.

                                                                                  Результат конечно будет такой, как вы ждёте, и вы вроде бы сэкономите время на написании кода, но когда проектом станет пользоваться чуть больше, чем 1 тысяча человек онлайе, вы сразу почувствуете просадку, потому-что сначала база возвращает в ORM все результаты от order by, а далее уже чаще всего исполняемый код делает все постпроверки where, вместо того, чтобы это делала база данных. Таким образом напрягается и база данных и сеть и сервер, который пересчитывает результаты, вместо всего одной БД, которая быстрее посчитала бы всё у себя в памяти.
                                                                                  Про IN я вообще молчу. В двух реализациях orm я видел, как она сначала делает select *, а потом пихает в IN все сотни тысяч полученных id в гигантский where in (1,2,3,4,5...), вместо простейшего left join.
                                                                                  Как итог — вы либо лезете в исходники изучать, почему orm делает такие странные запросы, либо гуглите, и находите с 10го раза правильный пример. Либо плюете, и пишете обычный и правильный SQL запрос.
                                                                                  Миграции — это отдельная тема. Очень странно, что ещё не изобрели нормальный git для структур бд.

                                                                                    +6

                                                                                    Вообще-то, нормальные ORM транслируют "where" "order by" (и пр.) в SQL код, так что фильтрацию и сортировку делает база.

                                                                                    • НЛО прилетело и опубликовало эту надпись здесь
                                                                                    +3
                                                                                    Нет, ну можно и без ORM жить… Но (говорю за Hibernate), когда ORM за тебя следит за изменением объектов текущей сессии и генерирует апдейты в базу — это удобно. Когда оно для тебя само делает Optimistic Locking — тоже хорошо. Когда Lazy Fetch можно аннотациями прописать — это тоже время экономит. Конечно, хотелось бы еще лучше (в пределе — одна волшебная кнопка «Сделать хорошо» — и все). Но это ж не повод отказываться от того неидеально хорошего, что есть!..
                                                                                      +1
                                                                                      Когда оно само (говорю за Hibernate) генерит хренову тучу лишних запросов — не очень хорошо.
                                                                                      0
                                                                                      В плане безопасности запросов SQL Mapper'ы не так, чтобы уступали ORM'ам.
                                                                                      Миграция/эволюция БД и ORM — ортогональны, много инструментов для первого обходятся без второго.
                                                                                      Хвалить ORM за то, что он позволяет не знать SQL и «лепить» запросы «на отвали» — тупиковое направление, ИМХО.
                                                                                      ORM чертовски удобен в типовых несложных случаях типа CRUD для нехитрого мастер-детали.
                                                                                      РДБ с их упором на уникальность фактов с их максимальной декомпозицией на атрибуты — ох, не всегда хороши. Иногда гораздо удобней «подшивать бумажки к делу» в документ-ориентированное хранилище: документ как контейнер, служит более эффективной заменой многим джойнам и связям. Но да, надо следить за тем, чтобы подшивать бумажки в правильные дела…
                                                                                        0

                                                                                        Как по мне, то как раз в сложных областях, которые плохо ложатся на CRUD без ORM очень грустно (при условии, что области хорошо ложится на ООП). В простых CRUD вполне можно на SQL писать.

                                                                                          0
                                                                                          В простых проектах в принципе всё равно как писать, все возможности ORM не всё равно раскрываются.

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

                                                                                          Оптимизации запросов же можно делать и средствами ORM.
                                                                                            0

                                                                                            Некоторые ORM подразумевают создание схем БД на основании объектной модели как основной кейс. А в общем случае задача ORM в том, чтобы связать две независимо созданные структуры: схему БД и объектную модель. Увы, не всегда это получается, ORM обычно протекают минимум в одну сторону: или в объектной модели торчит наружу реляционная природа, или в схеме базы странные вещи творятся с точки зрения нормальных форм и т. п. А иногда в обе стороны.

                                                                                              0

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

                                                                                                0

                                                                                                Например, в объектной модели у класса Order(заказ) есть свойство типа массив lines со строками заказа типа товар+количество. В объектной модели строке ссылка на заказ не нужна, как и идентификатор самой строки (достаточно индекса строки в массиве). Не во всех SQL базах вы сможете напрямую создать в таблице order столбец lines, являющийся упорядоченным множеством пар [itemId: quantity], а если сможете, то работать с ним будет неудобно, могут, например, отсутствовать возможности полноценной индексации такого поля, да и условие типа SELECT * FROM order WHERE EXISTS(order.items.itemId = 10523) вряд ли напишите.


                                                                                                Скорее всего вы создадите таблицу order_items, может с суррогатным PK, может с составным [orderId, index], причём эти колонки будут в любом случае, и скорее всего суррогатный id и поля orderId и index будут в вашем объекте, хотя на уровне объектной модели они вам не нужны вообще. Протечка реляционной модели в объектную налицо.

                                                                                                  0
                                                                                                  как и идентификатор самой строки

                                                                                                  У нее всегда есть идентификатор, это адрес в оперативной памяти. id это такой же адрес, только в более высокоуровневом логическом пространстве. Он присутствует в объекте как свойство, да, потому что убрать его совсем невозможно, так как надо работать с этим логическим пространством из приложения (select/insert/update/delete), или передавать объекты в сериализованном виде в другую систему (например на фронтенд). Это неотъемлемая часть модели, возникающая из-за дополнительного уровня абстракции. Работать с ним как с обычным свойством не совсем правильно, но это надо менять работу всех инструментов, которые с ним взаимодействуют.


                                                                                                  то работать с ним будет неудобно, могут, например, отсутствовать возможности полноценной индексации такого поля, да и условие типа SELECT * FROM order WHERE EXISTS(order.items.itemId = 10523) вряд ли напишите.

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

                                                                                                    0
                                                                                                    У нее всегда есть идентификатор, это адрес в оперативной памяти.

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


                                                                                                    id это такой же адрес, только в более высокоуровневом логическом пространстве. Он присутствует в объекте как свойство, да, потому что убрать его совсем невозможно, так как надо работать с этим логическим пространством из приложения (select/insert/update/delete), или передавать объекты в сериализованном виде в другую систему (например на фронтенд)

                                                                                                    Убрать его (высокоуровневый идентификатор строки, суррогатный или составной) совсем возможно вполне, если не допускать протечки реляционной модели через абстракцию ORM. ООП языки имеют свои способы идентифицировать объекты, отдельное свойство им для этого не нужно, а сам способо, опять же, деталь реализации. ORM может хранить карту "ссылка на объект <=> значение PK строки" у себя внутри, но это усложняет её, замедляет, и, возможно, есть ещё какие-то подводные камни, поэтому никто(?) так не делает.


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

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

                                                                                                      0
                                                                                                      ORM может хранить карту "ссылка на объект <=> значение PK строки" у себя внутри

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


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

                                                                                                      Так а как вы будете делать то, что написали выше? Для быстрого поиска среди строк всех заказов нужен индекс, а для него надо работать со строками как с отдельными сущностями. Не перебирать же все заказы чтобы найти те, в которых есть itemId = 10523. А если есть индекс, то по строке заказа надо как-то находить указатель на этот заказ. Или какой-то особый тип индекса вводить, где сразу будут указатели на родительскую сущность вместо той, которую индексируем.

                                                                                                        0

                                                                                                        Если объект ничего не знает о столбцах PK, то это не протечка. Если ORM о них знает, то это тоже не протечка. Собственно только ORM и должна знать о PK, и вообще таблицах и столбцах в приложении.


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

                                                                                                          0
                                                                                                          Собственно только ORM и должна знать о PK, и вообще таблицах и столбцах в приложении.

                                                                                                          А как на фронтенде сделать выпадающий список сущностей? Там тоже id нужен.

                                                                                                            –1

                                                                                                            Зачем? Индекса недостаточно?

                                                                                                              0

                                                                                                              Создаем товар, надо указать категорию. Создаем задачу, надо указать пользователя, который ее будет выполнять. Или фильтры различные.

                                                                                                                0

                                                                                                                в option мы не обязаны указывать в value идентификатор, вполне можем индекс массива, что-то вроде


                                                                                                                {goods.availableCategories
                                                                                                                  .filter(category => category.isActive)
                                                                                                                  .map((category, index) => (
                                                                                                                    <option value={index}>{category.name} />
                                                                                                                  )
                                                                                                                )}
                                                                                                                  +1

                                                                                                                  Ну вот у вас поле для ввода ответственного за задачу с автодополнением, пользователь ввел "Вася", пришло 3 варианта ["Вася Иванов", "Вася Петров", "Вася Пупкин"]. Пользователь выбрал "Вася Пупкин", на сервер ушел индекс 2. Что серверу с ним делать?

                                                                                                                    –1

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


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

                                                                                                                      +1

                                                                                                                      Можно, но зачем? Мы этим просто имитируем платформенно-независимый первичный ключ. Зачем так делать, если он уже есть, он для этого и нужен. А что если у нас 2 Васи Пупкина в разных отделах? Или добавили нового, пока пользователь задачу создавал?


                                                                                                                      Я не расширяю рамки, я же сразу написал, "передавать объекты в другую систему (например на фронтенд)". Необходимость этого означает, что о PK должна знать не только ORM, а все слои, которые с ним работают.

                                                                                                                        –1

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


                                                                                                                        Нет, расширяете. Для передачи объекта в другую систему идентификатор не нужен. Если СУБД+ORM PK нужен это вовсе не значит, что он нужен остальным. Идентификатор объекта (использование термина PK — протечка модели РСУБД в объектную бэка и фронта и ресурсную HTTP, наверное) необходим, вроде как, только для полноценных сущностей, где важно сравнение на идентичность "по ссылке". Там где необходима идентичность по значению, "PK" не просто не нужен, но вреден, поскольку приводит к неоднозначности ситуаций, когда значение PK совпадает, а остальные данные нет и наоборот.

                                                                                                                          +1

                                                                                                                          А откуда вдруг значения взялись, разговор же был про объекты и их идентификаторы?

                                                                                                                            0

                                                                                                                            У объектов обычно есть три способа сравнения, проверки на идентичность:


                                                                                                                            • по "физической" ссылке в рамках ЯП
                                                                                                                            • по полю-идентификатору
                                                                                                                            • по значениям всех полей

                                                                                                                            Если говорить о коллекциях объектов, то есть ещё один: по какому-то указателю (в широком смысле слова) на место объекта в коллекции.

                                                                                                                              0

                                                                                                                              По значениям полей сравниваются составные значения, объекты-сущности по ним не сравниваются.


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

                                                                                                                                0

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


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

                                                                                                                                  0

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

                                                                                                0
                                                                                                Как по мне то иногда в orm переутяжеляют слово «объект». Почему бы не представить себе что этот объект например User не реальный человек за компьютером а таблица user в базе данных. Поэтому у этого объекта будет метод save() и не будет метода «лайкнуть»
                                                                                                Аналогично переутяжелено слово модель в MVC которая изначально в smolltalk касалась модели элемента управления на экране. Например кнопка нажата или не нажата. А теперь возведена чуть ли не в бизнес правила.
                                                                                                  0

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

                                                                                          0
                                                                                          Я видел много разных ORM, но не видел ни одного хорошего.

                                                                                          А как же LinqToSql или Entity Framework?


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

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


                                                                                          И как же частичная статическая проверка запросов компилятором и контекстные подсказки от IDE? Избыточно и совсем не нужно?

                                                                                            –1
                                                                                            А в каком виде держать данные в памяти, если вы запретили объекты?
                                                                                            Это где же я запрещал объекты? Более того, я специально акцентировал внимание на том, что они — наша неизбежность.

                                                                                            И как же частичная статическая проверка запросов компилятором и контекстные подсказки от IDE? Избыточно и совсем не нужно?
                                                                                            Это, конечно, вкусно, но честное слово, жить без этого можно. Впрочем, если следовать подходу «объекты как проекции фактов, а в базе факты», то будут вам и проверки компилятором, и подсказки от IDE.
                                                                                            0
                                                                                            В рамках дискуссии выражу свое мнение. ORM суть способ сериализации дерева объектов. Все косяки выползают из необходимости поддерживать версионность в неявном виде. Ну то есть версионность объектов у нас в коде не ведется штатно, а база по своей природе версионностью обладает. Тут мы взяли редактор, переименовали Object.foo в Object.bar, на этом редактирование кода закончилось, осталось только базы привести в консистентный с версиями объектов вид, и вот тут и заверте. На мой взгляд тут правилен подход (не могу найти сходу ссылок, но на хабре много раз описывался), что система должна пытаться просто привести базу в состояние, консистентное текущей версии объектов в коде, а когда этого сделать не удается — единожды применять ко всем своим живым базам миграционный скрипт, который после применения уходит в небытие. Бессхемные базы требуют такой процедуры фактически только при операциях переименования сущностей, запретив которые, мы сделаем отображение вполне себе бесшовным.
                                                                                              0

                                                                                              EntityFramework в .NET смотрели?
                                                                                              Там все это реализовано.

                                                                                                0
                                                                                                Да много разных инструментов, я про задачу приведения базы к объектной схеме. Странно, что вообще мейнстрим для миграций до сих пор — это changelog, т.е. цепочки миграций бесконечные.

                                                                                                Лично мне интересно как в разных системах (не chengelog) с переименованием сущностей справляются, победив эту проблему можно вообще migration-less ormы делать.
                                                                                              0

                                                                                              Как по мне, то начиная говорить об ORM или его отсутствии нужно сразу объявлять о чём конкретно говорим:


                                                                                              • универсальные ORM
                                                                                              • полное отсутствие ORM (вообще полное, результат или исходные данные запроса никогда в объектной форме не предстают, ну или предстают, но никогда колонки не маппятся на свойства, а строки на инстансы, только какие-то Query.execute.getRow(0).getColumn('id').
                                                                                              • комбинации, в том числе ad-hoc ORM, когда результаты запросов маппятся на объекты "ручками", хардкодом

                                                                                              Отдельно отделять слой DBAL, который ни является ни необходимым, ни достаточным для ORM

                                                                                                0
                                                                                                А чем вам возвращаемый запросом массив записей не объект? В используемых Вами языках у него даже базовый класс есть — object?
                                                                                                  0

                                                                                                  В одном (плюс его надмножество) есть базовый Object у массивов, в другом, где ORM как раз и использую, массивы отдельный от объектов комплексный тип данных. Но под ORM я имею в виду прежде всего маппинг столбцов таблиц на свойства объектов и наоборот, а не обёртывание таблиц и их связей в объектную модель, в которой больше от двумерного массива.

                                                                                                +3

                                                                                                Открою тайну.
                                                                                                Любой ORM состоит из 3 частей:
                                                                                                1) Маппер (Mapper) — отображает резалтсет на объекты
                                                                                                2) Построитель запросов (Change Tracker) — генерирует SELECT\INSERT\UPDATE\DELETE на основе объектов и объектного языка запросов
                                                                                                3) Карта объектов (Identity Map + Change Tracker), которая следит чтобы одна запись в базе превращалась в один объект в памяти, а изменения объекта в памяти корректно "уезжали" в базу.


                                                                                                Маппер — самая примитивная вещи и проблем не представляет.
                                                                                                Построитель запросов — самая полезная вещь, но и самая сложная вещь в реализации
                                                                                                Карта объектов — самая сомнительная вещи, полезность которой постоянно оспаривается даже в этой статье.


                                                                                                Умные разработчики давно это поняли и просто ОТКЛЮЧИЛИ карту объектов по умолчанию в своих ORM. То есть вы строите запрос и получаете набор объектов, которые просто DTO и не имеют другой смысловой нагрузки.


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


                                                                                                Уже лет 10 вся прогрессивная общественность пользуется такими легковесными ORM. Но некоторые продолжают обсуждать недостатки карты объектов и приписывать всем частям ORM.

                                                                                                  0

                                                                                                  Маппер далеко не всегда тривиален и проблем не представляет. Даже отображение 1:1 строки на объект может быть не тривиальным, если требуется одновременно сделать его и быстрым, и работающим с приватными свойствами объекта без вызова его конструктора и в остуствие геттеров/сеттеров. А если из строки таблицы нужно собрать многоуровневый граф объектов, да с возможностью поиска по любому объекту на SQL по индексу, то совсем нетривально, если вообще возможно. И это ещё не касаясь отношений между объектами в разных таблицах и наследования.

                                                                                                    0

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

                                                                                                      0

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

                                                                                                    0

                                                                                                    Заметил опечатку.
                                                                                                    Построитель запросов конечно Query Builder, а не Change Tracker

                                                                                                      0
                                                                                                      ORM без карты объектов — это уже не совсем ORM, имхо. Не сильно отличается от обычного SQL маппера: если дважды вызвать один и тот же запрос, то возвращенные записи не будут связаны и их можно менять и сохранять независимо со всеми вытекающими. Спор о словах, конечно, но мне кажется, что граница именно тут проходит: есть контроль загруженных объектов — ORM, нет — уже что-то более простое.
                                                                                                        0

                                                                                                        IdentityMap не является обязательной для ORM. С ней меньше одних проблем, но больше других.

                                                                                                          0

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

                                                                                                            0
                                                                                                            В правильно используемом ORM это отображение результата запроса к базе данных на единственный объект в программе. Для этого и нужен маппинг. Если условие единственности нарушается, то возможно рассогласование между базой данных и разными частями программы.
                                                                                                              +1

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

                                                                                                                0

                                                                                                                М.б. имеется в виду контроль единственности не объекта, а класса?

                                                                                                                  0

                                                                                                                  Вряд ли, речь же про результат запроса.

                                                                                                                  0

                                                                                                                  По факту разница для прикладного разработчика в том, что в рамках одного процесса мы можем устанавливать идентичность двух объектов по ссылке. А для разработчиков ORM использование IdentityMap позволяет в большинстве случаев переложить ответственность за изменения БД на прикладного разработчик. Если он перезаписывает свойства заведомо одного и того же объекта несколько раз в разных частях приложения, то не должен удивляться, что в базу запишется только последнее значение на момент вызова save или flush. А если отдавать разные объекты, то разработчика ORM придётся разруливать конфликты.

                                                                                                                    0

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

                                                                                                                      0

                                                                                                                      Чтобы улучшить привлекательность своих продуктов для разработчиков. Суть подобных продуктов — упростить жизнь прикладных разработчиков. IdentityMap в составе ORM даже неопциональная, по-моему, значительно упрощает.

                                                                                                                        0

                                                                                                                        А можете пример написать, где упрощает? Я довольно много работал с ActiveRecord, приложения среднего уровня, вроде не было случаев где бы это понадобилось. Обычно объект достается из базы 1 раз и передается внутри приложения по ссылке.

                                                                                                                          0

                                                                                                                          Ну вот представьте сущность договор, у которой есть поля создатель и подписант (ссылки на сущность "работник", например). Мы выбираем договор и получаем два разных объекта с идентичным состоянием. В чём ценность такого кейса? Как должна реагировать ORM если на запись приходит договор в котором один объект изменён (но не id), а другой нет? А если оба изменены? Да просто как-то проверить что если создатель и подписант одинаковые, то направить договор на дополнительный апрув.

                                                                                                                            0

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

                                                                                                                              0

                                                                                                                              1) Бизнес решает, что должно быть, а чего нет, а не мы, разработчики. Если бизнес решает, что должно, а мы отказываемся лишь потому что считаем это неправильным и не можем убедить бизнес в этом, то нас обычно увольняют.
                                                                                                                              2) Если бизнес хочет, чтобы на экране редактирования договора были указаны ФИО создателя и подписанта, то одним id вы не обойдётесь
                                                                                                                              3) "раз у нас есть" — нету, рассматриваем вопрос стоит ли его вводить и не просто вводить, но и прокидывать через все слои приложения.


                                                                                                                              P.S. Я очень не одобряю подход: "раз необходимо или почему-то есть на одном уровне приложения, значит прокинем на все" — это прямое нарушение инкапсуляции. Вот прокидываете вы умолчанию числовой id на все слои, а тут раз и появляется требование сделать его строковым да в чётком формате. Или наоборот. Все слои придётся изменять, даже если по факту это поле не используется для бизнес-логики, но используется в технических вещах типа тестов, технической валидации (в том числе средствами языка или вншнего тайпчекера) и т. п.

                                                                                                                                0
                                                                                                                                Если бизнес решает, что должно, а мы отказываемся

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


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

                                                                                                                                А по какому критерию вы будете объекты из базы доставать, чтобы положить в IdentityMap? Она ведь задает соответствие id и указателя.


                                                                                                                                Я очень не одобряю подход: "раз необходимо или почему-то есть на одном уровне приложения, значит прокинем на все" — это прямое нарушение инкапсуляции

                                                                                                                                Так не надо его считать деталью реализации) У нас объекты независимые от платформы, передаются между разными системами, надо чтобы разные системы могли каким-то образом идентифицировать один и тот же объект. Вот и "указатели" на эти объекты такие же независимые от платформы. Это как пытаться избавиться от указателей в оперативной памяти в пределах одного приложения.
                                                                                                                                Уж что-что, а индекс в коллекции зависит от реализации практически полностью. Поменялись требования к сортировке в интерфейсе — менять надо везде.


                                                                                                                                Все слои придётся изменять

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

                                                                                                                                  0
                                                                                                                                  А по какому критерию вы будете объекты из базы доставать, чтобы положить в IdentityMap? Она ведь задает соответствие id и указателя.

                                                                                                                                  На уровне базы есть. ORM о нём знает и активно использует, в том числе для IdentityMap. А дальше если он остальным слоям не нужен, то и не нужно его на них прокидывать. Ещё ситуация, когда не нужно PK прокидывать — у нас есть не только первичный (часто суррогатный) ключ, но и вторичный (естественный для предметной области). Заем вышележащим слоям знать о первичном, когда задача идентификации объектов решается вторичным на бизнес-уровне?


                                                                                                                                  Вот и "указатели" на эти объекты такие же независимые от платформы.

                                                                                                                                  Это не единственный способ идентификации.


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

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


                                                                                                                                  Естественно, бизнес-задача же изменилась.

                                                                                                                                  Бизнес-задача не изменилась, бизнес же о них не знает, в бизнес-логике их нет, требований к ним нет. :)

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

                                                                                                                                    Нет, подождите. У нас приходит с фронтенда какая-то информация о параметрах договора "Создатель" и "Подписант", но не id. Что это за информация, которая однозначно их идентифицирует, позволяет загрузить из базы в IdentityMap, и применить к ним изменения из формы?


                                                                                                                                    Это не единственный способ идентификации.

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


                                                                                                                                    Зачем вышележащим слоям знать о первичном, когда задача идентификации объектов решается вторичным на бизнес-уровне?

                                                                                                                                    Если он однозначно идентифицирует объект, то незачем, но и разницы с числовым id никакой нет. Это точно так же логический указатель, адрес в логическом пространстве.


                                                                                                                                    а наружу ссылки, а не указатели

                                                                                                                                    Я подразумевал их как синонимы.


                                                                                                                                    Бизнес-задача не изменилась, бизнес же о них не знает

                                                                                                                                    Как это не знает, если "раз и появляется требование сделать его строковым да в чётком формате"? Это ведь требование бизнеса.

                                                                                                                                      0
                                                                                                                                      У нас приходит с фронтенда какая-то информация о параметрах договора "Создатель" и "Подписант", но не id.

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


                                                                                                                                      Это естественный для объектов способ идентификации.

                                                                                                                                      Ничего естественного в нём нет для объектов.


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

                                                                                                                                      Полностью не согласен. Первичный ключ — термин исключительно РСУБД (может других СУБД, но мы не о них) для идентификации кортежа в реляции.


                                                                                                                                      Как это не знает, если "раз и появляется требование сделать его строковым да в чётком формате"? Это ведь требование бизнеса.

                                                                                                                                      Это может быть требование DBA или фронтендеров. :)

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

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


                                                                                                                                        Ничего естественного в нём нет для объектов.

                                                                                                                                        Объекты отличаются от значений только наличием первичного ключа.


                                                                                                                                        Первичный ключ — термин исключительно РСУБД

                                                                                                                                        РСУБД основаны на математике, первичный ключ это свойство данных, а не деталь реализации СУБД, он есть в теории баз данных.


                                                                                                                                        Это может быть требование DBA или фронтендеров

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

                                                                                                                                          0
                                                                                                                                          ИНН или табельный номер вообще ничем от числового id из базы не отличаются, это и есть придуманные суррогатные ключи чтобы различать объекты.

                                                                                                                                          Отличаются. Как минимум ИНН для подавляющего большинства приложений — это внешний ключ.


                                                                                                                                          РСУБД основаны на математике, первичный ключ это свойство данных, а не деталь реализации СУБД, он есть в теории баз данных.

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


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

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

                                                                                                                                            0
                                                                                                                                            Как минимум ИНН для подавляющего большинства приложений — это внешний ключ.

                                                                                                                                            Ну и? Идентификация объектов от этого никак не меняется, будете ли вы использовать числовой id в качестве ключа или числовой ИНН. Люди придумали ИНН ровно по тем же причинам и для тех же целей, для которых делают суррогатный числовой id для других сущностей.


                                                                                                                                            Это не свойство данных, это свойство реляционной модели данных.

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


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

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

                                                                                                                                              –1
                                                                                                                                              Нет. Это свойство вытекает из самой сути объекта — у него есть состояние и поведение, значит должна быть неизменяемая часть, чтобы в разные моменты времени можно было определить, что объект "тот же самый", что это не новый объект, а ранее известный объект изменил состояние.

                                                                                                                                              Вот когда по задаче надо определять тот же самый объект или нет, тогда и должен быть выденный идентификатор. Зачем он нам, если не надо определять или надо, но вполне хватает средств языка?


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

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