ORM или объектно-реляционный проектор

    Сегодня мы предлагаем вашему вниманию отрывок из книги Сергея Тарасова «Дефрагментация мозга. Софтостроение изнутри», которая готовится к выходу в нашем издательстве.

    Сокрытие базы данных или как скрестить ёжа с ужом


    Упомянув один из крупнейших столпов современного софтостроения — мир ООП, нельзя обойти вниманием и другой — мир реляционных баз данных. Я намеренно вставил прилагательное «реляционные» применительно ко всем основным СУБД, хотя ещё в 1970-х годах такое обобщение было бы неправомерным.

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


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

    И реляционная и объектная модели относятся к логическому уровню проектирования программной системы. Они ортогональны и по сути представляют собой два взгляда на одну и ту же сущность. Это значит, что вы можете реализовать ту же систему оставаясь в рамках только одного реляционно-процедурного подхода или же следуя исключительно ООП.
    На практике сложилась ситуация, когда программы пишутся в основном с использованием ООП, тогда как данные хранятся в реляционных БД. Не касаясь пока вопроса целесообразности такого скрещивания «ёжа с ужом», примем ситуацию как данность. Из которой следует необходимость отображения (проецирования) объектов на реляционные структуры и обратно.

    Ввиду упомянутого отсутствия под ООП формальной теоретической базы эта задача нерешаемая в общем случае, но выполнима в частных. Компонент программной системы, реализующий отображение, называется ORM (Object-Relational Mapping) или объектно-реляционный проектор — ОРП. Полноценный ORM может быть весьма нетривиальным компонентом, превышающим сложность остальной системы. Поэтому хотя многие разработчики с успехом пользуются своими собственными частными реализациями, в отрасли за последние 10 лет появилось несколько широко используемых фреймворков, выполняющих в том числе и задачу проекции.

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

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

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

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

    Такая ситуация приводит разработчиков ORM к необходимости создавать собственный SQL-подобный язык для манипуляции объектами и уже его транслировать в сиквел (HQL — Hibernate Query Language – SQL-подобный язык запросов, используемый в Hibernate/NHibernate ). Или использовать непосредственно SQL с динамическим преобразованием результата в коллекцию объектов.

    В противном случае прикладной программист обречён на извлечение из БД и последующую обработку больших массивов данных непосредственно в своём приложении. Примерно так же обрабатывали табличные данные при отсутствии встроенного SQL разработчики на ранних версиях Clipper в конце 80-х годов. Там это называлось «навигационная обработка». Думаю, термин уместен и здесь.

    В эпоху массового перехода с Clipper-подобных программ и файл-серверных технологий на клиент-серверные РСУБД многие приложения и их разработчики продолжали использовать навигационный подход. Приложения работали медленно, зачастую блокируя работу в многопользовательской среде. Потому что для эффективной работы с РСУБД нужно использовать подходы, ориентированные на обработку множеств на сервере, предполагающие наличие у разработчика умений работать с декларативными языками.

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

    Почему? Недостаток знаний РСУБД пытаются заместить дополнительными уровнями абстракций. На деле же выходит обратное: уровни абстракции скрывают не детали слоя хранения объектов от программиста, а наоборот, его некомпетентность в области баз данных от СУБД. До некоторого времени.

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

    Но попытки продолжаются. Одни по-прежнему разрабатывают проекторы для своих внутренних нужд, зачастую очень лёгкие. Другие ищут упрощение и выход в noSQL. Но в выигрыше пока остаются имеющие «базоданные» компетенции программисты и консультанты, зарабатывающие на тех, кто ими не обладает.

    Как обычно используют ORM


    На софтостроительных презентациях часто рисуют красивые схемы по разделению слоёв представления, бизнес-логики и хранимых данных. Голубая мечта начинающего программиста – использовать только одну среду и язык для разработки всех слоёв и забыть про необходимость знаний реляционных СУБД, сведя их назначение к некоей «интеллектуальной файловой системе». Слово SQL вызывает негативные ассоциации, связанные с чем-то древним, не говоря уже про триггеры или хранимые процедуры. На горизонте появляются добрые люди, с книгами разных гуру о домен-ориентированой разработке под мышкой, заявляющие новичкам примерно следующее: «Ребята, реляционные СУБД — пережиток затянувшейся эпохи 30-летней давности. Сейчас всё строится на ООП. И есть чудесная штука — ORM. Начните использовать ее и забудьте про тяжёлое наследие прошлого!»

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

    Начинаем реализовывать модель предметной области. Добавляем классы, свойства, связи. Генерируем структуру базы данных или подключаемся к существующей. Строим интерфейс управления объектами типа CRUD. Все достаточно просто. По крайней мере кажется вполне сравнимым с манипуляциями над DataSet. Тем кто о них знает, конечно, ведь не все подозревают о существовании табличных форм жизни данных в приложении за пределами сеток отображения DBGrid.

    Как только разработчики реализовали CRUD-логику, начинается основное действо. Использовать сиквел напрямую теперь затруднительно. Не касаясь стратегий отображения и проблем переносимости приложения между СУБД, по сути каждый SQL-запрос с соединениями, поднявшись в домен, сопровождается специфической проекцией табличного результата на созданный по этому случаю класс. Поэтому приходится использовать собственный язык запросов ORM. Нестандартный, без средств отладки и профилирования. Если он, язык, вообще имеется в данном ORM. Для поддерживающих соответствующую интеграцию среда .NET даёт возможность использовать LINQ, позволяющий отловить некоторые ошибки на стадии компиляции.

    Сравните выразительность языка на простом примере, который я оставлю без комментариев.

    SQL
    SELECT *
    FROM task_queue
    WHERE
      id_task IN (2, 3, 15) 
      AND id_task_origin = 10
    


    NHibernate HQL
    IList<TaskQueue> queues = session
      .CreateQuery("from TaskQueue where Task.Id in (2, 3, 15) and TaskOrigin.Id = 10")
      .List<TaskQueue>();
    


    NHibernate без HQL с критериями
    IList<TaskQueue> queues = session.CreateCriteria()
      .Add(Expression.In("Task.Id", someTasks.ToArray()))
      .Add(Expression.Eq("TaskOrigin.Id", 10))
      .List<TaskQueue>();
    
    

    LINQ (NHibernate)
    IList<TaskQueue> queues = session
      .Query<TaskQueue>()
      .Where(q => someTasks.Contains(q.Task.Id) &&
             q.TaskOrigin.Id == 10).ToList();
    


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

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

    Издательский дом «Питер»
    160.08
    Company
    Share post

    Comments 50

      0
      Интересно! Давно хотел почитать какую-нибудь книгу именно о философии практик программирования, ведь в современных подходах просто до ужаса много общего, и всегда интересно знать мнение товарищей, чей опыт использования отличается от собственного. Надеюсь, в продолжении книги этот лейтмотив развивается, а не остается на уровне тривиальных примеров.
        +1
        «Использовать сиквел напрямую теперь затруднительно»
        Замечал, что некоторые люди говорят «сиквел», а некоторые «скуль».

        А вообще приятное изложение, даже немного обидно, что отрывок закончился слишком внезапно. Когда книга выйдет?
          +5
          Если всё будет по плану, то в марте.

          Я могу выложить еще главу на следующей неделе.
            +1
            Спасибо за информацию. Да, мне было бы интересно почитать ещё.
              +1
              Да, выложите, пожалуйста, ещё главу. Очень интересно написано. И ещё, если можно, поделитесь полным содержанием книги, интересно какие ещё темы затронуты.
              +1
              А будут ли в книге освещены ситуации, сталкиваясь с которыми разработчики предпочитают не использовать в каком-то конкретном проекте ORM? Конечно красиво написано в примере в книжке, как удобно доставать salary of employee in department, но в жизни разработчики сталкиваются с гораздо более сложными и мудреными задачами.
                0
                еще +1 за выкладывание
                  0
                  habrahabr.ru/company/piter/blog/165757/
                  Еще главка и содержание
                    0
                    404 там…
              +2
              Этот пример можно написать и в более симпатичном виде.
              var queues = from t in session.Query<TaskQueue>()
                                       where someTask.Contains(t.Task.Id) && q.TaskOrigin.Id == 10
                                       select t;

              А попробуйте написать такой запрос на SQL в более понятном виде:
                          var queues = from t in session.Query<TaskQueue>()
                                       where someTask.Contains(t.Task.Id) 
                                              && q.Client.Address.Country.Languages.Contains(englishLanguageId)    
                                       select t;



                0
                У меня на php так:

                $taskQueues = \TaskQueue\Model::select()
                        ->withTaskId([1, 2, 3])
                        ->withTaskOrigin(10)
                        ->getCollection();
                


                Второй ваш запрос я не понял, честно, но наверное, потому, что с этой ORM я не знаком.

                Но если простой join, то примерно так:
                $clientSelector = \Client\Model::select()
                        ->withNameNotContains('Артур');
                
                $taskQueues = \TaskQueue\Model::select()
                        ->join($clientSelector)
                        ->getCollection();
                


                ну или так:
                $taskQueues = \TaskQueue\Model::select()
                        ->join(\Client\Model::select()->withNameNotContains('Артур'))
                        ->getCollection();
                

                Но предыдущий вариант, мне нравиться больше, если условий больше.
                  0
                  Во втором случае там не просто Join, а выборка задач клиентов, проживающих в стране, у которой среди государственных языков присутствует английский. Ну т.е. 4 join'а.

                  В этом, кстати, кроется проблема ORM — дырявая абстракция. Да, Linq2SQL/EF сам сгенерирует правильный запрос, и в некоторых случаях даже более оптимальный, чем написали бы некоторые программисты, но иногда люди забывают, что кроется за «Client.Address.Country.Languages.Contains», и что вероятнее всего есть более простой и оптимальный способ решения задачи.
                    0
                    Спасибо за информацию, попробую у себя как-то такое реализовать, думаю поможет разрабатывать быстрее.

                    Скажите, а IDE поддерживают автокомплит такой записи — «q.Client.Address.Country.Languages.Contains»? Я люблю записи в виде цепочки, но, если IDE мне помогает) Просто не знаю, в рамках какого языка это пример, но что-то похожее на C# или Java.
                      0
                      Разумеется, автокомплит поддерживается, как и проверка выражений при компиляции программы.
                      Фокус в том, что на каждую таблицу БД создаётся класс. Поля класса соответствуют столбцам таблицы.

                      Если в таблице Q есть поле ClientId, которое указывает на Id в таблице Clients, то в ORM-классе появляется ссылочное поле Client, при обращении к которому (например, в выражении q.Client.FIO) в поле q.Client прозрачно подгружается запись из таблицы Clients по нужному Id, и выражение q.Client.FIO — это уже ФИО клиента из таблицы клиентов. Естественно, есть кеш и любые обращения к уже загруженным записям читают из кеша.
                        0
                        Ага, уже понятнее. Вы меня остановите пожалуйста, если я тут флуд устраиваю. Ссылочное поле Client появляется при прекомпиляции или его заводит программист?
                          0
                          Есть три подхода к генерации ORM-классов:

                          1. Программист пишет классы полностью вручную, и поля с примитивными типами данных, и ссылочные. Затем настраивает — указывает, что класс Client сопоставлен с таблицей TBL_CLIENTS, а поле Client в классе Order соответствует записи в таблице TBL_CLIENTS, загружаемой по значению из столбца CLIENT_ID таблицы TBL_ORDERS.

                          Могут быть и обратные связи: например, коллекция Orders в классе Client собирается по ссылкам на эту запись в TBL_CLIENTS из поля CLIENT_ID таблицы TBL_ORDERS. То есть, в таблице TBL_CLIENTS нет никаких упоминаний о заказах, но в классе Client есть коллекция заказов данного клиента.

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

                          2. Программист рисует UML-модель (сущности и связи между ними), ORM по модели генерирует классы и БД

                          3. Программист натравливает ORM на существующую базу, она генерирует прокси-классы.

                          Некоторые ORM поддерживают все 3 подхода, некоторые — только 1-й или 3-й
                            0
                            Понятно, главное, что никакой магии с полем Client нет, это просто public свойство Q верно?
                              0
                              Да, за исключением того, что обычно все ORM-поля не обычные поля, а свойства.
                              Т.е. при чтении вызывается код getField при записи setField

                              ORM накладывает свои обработчики на эти методы (требуя, чтобы свойства были описаны как virtual, а клиенту при загрузке из базы отдавая не чистые классы, а наследников, где эти методы уже перегружены). Клиент не замечает, что это наследники и работает с загруженными классами как со своими. В обработчиках get{Field} ORM подгружает данные при первом обращении к ссылочному полю, а в set{Field} помечает у себя, что перед закрытием транзакции нужно сделать запись изменений в БД.

                              Технологически современный ORM не так прост, потому что подразумевает кодогенерацию классов-наследников.
                      0
                      >>>но иногда люди забывают, что кроется за «Client.Address.Country.Languages.Contains»

                      Нет ли такой проблемы в любом средстве высокого уровня?
                        0
                        Да, конечно, такая проблема есть всегда, когда поверх одной системы строится другая, чуть более упрощенная и призванная скрыть детали реализации первой. Закон дырявых абстракций, как назвал это Джоэль Спольски. Согласен, что это не имеет прямого отношения к концепции ORM. Просто хотел напомнить, что у любого инструмента, у ORM, у SQL, у использованного фреймворка, у использованных протоколов, у железа, и т.п. есть ограничения и узкие места, о которых нужно помнить, и что серебряной пули не может быть. Причем, чем сложнее сценарий использования, тем чаще натыкаешься на узкое место, поэтому постоянно приходится балансировать между полученными выигрышем по одним из критериев и проигрышем по другим.
                  +4
                  В моей команде ORM не используется, у нас есть аналитики – эксперты в областе SQL и различных СУБД, которые проектируют БД и строят запросы. Эти запросы переносятся в код практически без изменений, и они могут его отлаживать, изменять в случае необходимости. С ORM это было бы затруднительно.
                    +1
                    А как вы потом результаты запросов скрещиваете с объектами? Руками?
                      0
                      Результаты запросов интерпретируются как массивы данных, объекты тут больше не причем.
                        0
                        а как к полям обращаетесь в этих массивах? по индексу??
                          0
                          по названию например.
                            +1
                            Т.е. слоя модели у вас в коде нет, и во всех других слоях Вы опрерируете просто полученными массивами?
                              0
                              1. Я не говорил про MVC.
                              2. У MVC есть множество модификаций, трактовок.
                    +4
                    Как можно сравнивать ORM и SQL? ORM помимо самой выборки еще занимается отображением выбранных данных в классы, что избавляет нас от лишнего кода. Элегантный пример sql запроса не отобразит нам полученные данные в классы для последующей обработки в приложении.

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

                    На текущем проекте мы перешли на использование легковесного tiny orm PetaPoco — все очень быстро и хорошо.
                      +1
                      Нормальная ORM вам отобразит данные, возвращенные элегантным примером sql-запроса.
                        0
                        > Кроме того, существуют хранимые процедуры для написания сложных запросов в случаях, когда оптимальность запросов ORM ставится под вопрос.

                        Главное с этим не переусердствовать и не наплодить хранимых процедур, а то ORM со своим кэшированием будет совсем уж не к месту (а без кэширования лучше юзать что-нибудь попроще).
                          0
                          PetaPoco это совсем micro ORM, представляет с собой один файл с открытым кодом. Сейчас готовлю статью на эту тему.
                        +1
                        SQL
                        SELECT *
                        FROM task_queue
                        WHERE
                          id_task IN (2, 3, 15) 
                          AND id_task_origin = 10
                        


                        JQL
                        SELECT tq
                        FROM taskQueue tq
                        WHERE
                          tq.id_task IN (2, 3, 15) 
                          AND tq.id_task_origin = 10
                        


                        В случае SpringData:
                        public interface taskQueueDao extends CrudRepository<taskQueue, Long> {
                           @Query("SELECT tq FROM taskQueue tq WHERE  tq.id_task IN (2, 3, 15)  AND tq.id_task_origin = 10")
                          public List<taskQueue> getTaskQueue();
                        } 
                        


                        JQL входит в стандарт JPA. Для оптимизации запроса можно крутить хинты и указывать где использовать жадную загрузку и join.

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

                        К тому же любой нормальный ORM позволяет составлять запросы руками.

                        Технически же определить хороший или плохой перед вами ORM можно всего по двум критериям

                        1. Генерация domain классов по базе.
                        2. Наличие возможности использования SQL в запросах при необходимости.

                        Я еще не видел ни одного плохого ORM который бы включал эту функциональность. Генерация же метаданных только в одну сторону повод задуматься, а все ли хорошо с этим ORM.
                          +2
                          А я пользуюсь СУБД Caché, которая одновременно предоставляет и объектный, и реляционный, и прямой доступ.
                          Встроенная поддержка взаимного отображения, например классов в таблицы и таблиц в классы, позволяет в SQL использовать объектные расширения из любого ODBC/JDBC клиента.
                          Подробности можно почитать в документации.
                            +2
                            мне показалось, что тема не раскрыта.
                              +1
                              Очень и очень однобоко.

                              1) ORM и называется Mapper, потому что в первую очередь отображает данные из одного «пространства» в «другое» и предоставляет унифицированный интерфейс, а не генерирует запросы. С этой стороны их и надо рассматривать в первую очередь (как orm позволяет представлять модели, как работают миграции, во сколько обходится изменение модели итп итд).

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

                              3) Как уже писали выше и как завещал Фаулер, никто не запрещает оптимизировать узкие места с помощью хранимых процедур. Если эта оптимизация оправдана и не выливается в издержки, превышающие business value оптимизации.

                              Единественно с чем соглашусь с автором — действительно, для очень многих задач для которых хорошо работают ORM использование РСУБД в связке с ORM являет сильным оверхедом. Очень часто хватает NoSQL-хранилища и легковесного маппера вокруг него.
                                0
                                «Однако, запросы типа «выбрать сотрудников, зарплата которых в течение последнего года не превышала среднюю за предыдущий год» уже вызывают проблемы на уровне встроенного языка.»

                                Не вижу коренной разницы в том как будет написан linq-запрос и соответствующий ему sql. И как он будет сгенерирован вменяемой ORM.
                                  –1
                                  Автор забыл написать юс-кейс, где мощь ORM раскрывается наиболее полно.
                                  А именно, сложная логика с циклами, ветвлениями и обновлениями данных.
                                  Например,
                                  foreach (var ware in order.Wares) {
                                    if (ware.Price > 100 && order.Customer.City == CityCodes.Moscow) {
                                      order.BonusWares.Add(new BonusWare(order, WareCodes.SamsungTV);
                                      ware.Price *= 2;
                                    }
                                  }

                                  Страшно представить, как этот код выглядел бы на SQL, с его declare cursor и циклом open… while… fetch, или на голом c# с insert/update запросами средствами ADODB.Command. А если бизнес-правил в коде, типа вышеприведённого — 1.5 страницы, то переписать на голом SQL/ADODB, а потом это ещё и поддерживать — быстрее застрелиться.

                                  Разумеется, запросы типа «выбрать сотрудников, зарплата которых в течение последнего года не превышала среднюю за предыдущий год» — не конёк ORM. Зачем автор в этом контексте вообще о них вспомнил.
                                    +1
                                    Эм то что вы написали отлично делается на SQL и без курсоров.
                                      0
                                      Да, но это сложнее поддерживать.

                                      И плохо контроллируется при усложнении задачи. Если внутри цикла не один if, а десяток (теоретически, тоже можно переписать без курсоров), то 20 строк, которые занимает 1 select, выглядеть будут очень страшно.
                                        0
                                        Да, но это сложнее поддерживать.

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

                                        И плохо контроллируется при усложнении задачи. Если внутри цикла не один if, а десяток (теоретически, тоже можно переписать без курсоров), то 20 строк, которые занимает 1 select, выглядеть будут очень страшно.

                                        В этом случае можно написать хранимку. Ваш пример хорош если у вас данных немного и вы их обрабатываете реально сложным алгоритмом.
                                    0
                                    «Как связать мир SQL и ООП? Какой 'мост' для это использовать?» Эти вопросы я уже не раз задавал, и в том числе на хабре, но ответа не было. Поэтому задаю еще раз: Как обеспечить взаимодействие ортогональных технологий? Как построить гибкую архитектуру, что бы проект можно было адаптировать к новым требованиям? Как снизить энтропию в нем? Как защитить существующий код от изменений? Как обеспечить модульность проекту?
                                    — Если использовать напрямую SQL запросы в коде, то получается, простите за выражение, говнокод. Если измениться структура данных — код проекта придется менять, и не просто менять, а менять в нескольких местах.
                                    — Если используешь ORM, то до определенного момента все идет как по маслу. Геморрой начинается когда проект уже завязан на ORM, и оказывается что в ней не хватает какой-то функциональности. Тогда начинаешь просто волосы рвать на себе, за то что выбрал эту ORM, а не другую (у которой проблем ни чуть ни меньше). Да существуют очень крутые ORM, но как уже было написано, они не выразительны с их собственным «SQL» языком.
                                    — Перенос части логики в БД по средствам хранимых процедур. Очень привлекательный метод. Однако как минимум появляется привязка к конкретной СУБД. Появляются трудности с отладкой. Обеспечить модульность проекта не очень просто. В одном из своих проектов я, ради эксперимента, превратил PostreSQL фактически в RPC сервер. Не могу сказать что результат мне понравился. Возможно я что-то не учел, или не правильно построил архитектуру.
                                    — Использование NoSQL. Сам не юзал. Но люди говорят, что там своих проблем куча.
                                    Какие есть еще походы? Какие есть еще соображения?
                                    P.S. По профессии я не программист — сисадмин я. Программированием занимаюсь как говориться «не ради пьянства окаянного, а дабы не отвыкнуть», поэтому многого не знаю и хотел бы услышать мнение других.
                                      +2
                                      1. Берешь myBatis
                                      2. Пишешь SQL
                                      3. Получаешь объекты
                                      4.…
                                      5. Profit!
                                        0
                                        В принципе не плохо. А кроме JAVA и .Net поддерживаются еще какие-нибудь языки? Или все остальные в пролете? Я использую C/C++/Python.
                                          0
                                          Не в курсе.

                                          Беглый просмотр интернетов не позволил найти аналогов myBatis для C++. Но это был бы весьма перспективный опенсурс проект.
                                      +1
                                      Автор не упомянул про myBatis и похожие технологии, которые могут быть полезны в определенных обстоятельствах. По сути, автор рассматривает 2 крайности и критикует одну из них.
                                        0
                                        Как-то мутно и непонятно. Может, кто-нибудь подскажет, на какой уровень читателей рассчитана эта книга? И что стоит почитать, чтобы достичь этого уровня?
                                        0
                                        Я так понимаю основной посыл — «ORM портит людей!» :)
                                          –1
                                          Плохие ORM, типа Entity Framework — портят.

                                        Only users with full accounts can post comments. Log in, please.