Подтвердить что ты не робот

Является ли ОРМ еще "Вьетнамом компьютерных наук"?

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

4b9b3361

Ответ 1

Это все еще верно.

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

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

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

RoR и шаблон ActiveRecord по праву заслужили репутацию бонуса ресурсов dbms. Оптимизированный дизайн ActiveRecord чаще всего является субоптимальным SQL-дизайном, поскольку он поощряет разложение SQL-предложений.

Ответ 2

Да.

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

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

Во всех программах существует компромисс между гибкостью и предположениями. Рамки (например, Rails) пытаются решить проблему, будучи "самоуверенной". То есть они ограничивают гибкость как реляционных, так и объектно-ориентированных аспектов проблемы, делая предположения о том, как структурированы данные и какие операции вы можете с ней делать. Естественно, упрощение проблемного пространства делает решение более простым.

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

Ответ 3

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

Если ORM является "вьетнам компьютерной науки", то создание собственного хранилища ключей - вероятно, "Ирак компьютерных наук": -)

Ответ 4

Я могу говорить только за свой опыт. Я использую DAL и DTO, и я все еще способен выполнять довольно сложные запросы (объединения и все), а также я могу прибегать к SP или настраиваемому SQL всякий раз, когда мне нужно. Это облегчило мою жизнь, мой код был согласован, и мои сроки были более достижимыми.

Ответ 5

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

База данных обязательно является низкоуровневой; он хранит числа и строки по существу. Бизнес-логика является высокоуровневой. Вот почему у нас есть абстракция.

Лично я считаю, что путь Rails/ActiveRecord является наилучшим решением для наличия модели объекта/домена, но также может использовать реляционную базу данных.

Итак: не выбрасывайте ORM, но не делайте по умолчанию его. Это инструмент, который решает некоторые проблемы. Чтобы игнорировать это было бы невежественным, и всегда использовать это было бы высокомерным.

Ответ 6

Статья Джеффа ссылается на статью Теда Ньюарда. Если вы входите в детали, вам нужно посмотреть там:

Из исходных точек Ted у меня это как:

  • 1 был неправильным (Identity)
  • 2 из них решены (Частичные объекты и N + 1)
  • 2 являются спорными (Двойная схема/общая схема).

Отказ от ответственности: я автор Ebean ORM, поэтому я расскажу об этом для различных "решений" поднятых проблем.

Исходные точки Ted (дистиллированные, потому что они действительно многословны):

1. Проблема с частичным объектом.

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

2. N + 1 (парадокс времени Ted Load)

Всегда разрешимо. Должно быть написано как 1 + N / batchSize, но это более интересно, чем это (на каждый путь нужно учитывать SQL-пейджинг, избегать sql-декартова продукта). Некоторые ORM делают правильный беспорядок этого, к сожалению, и это приносит ORM вообще дурную славу. Некоторые ORM подходят, пока вы не достигнете уровня сложности (например, OneToMany внутри OneToMany внутри OneToMany).

Просто для продолжения анте здесь ORM может профилировать использование графа объекта и автоматически оптимизировать запрос (только выборка, которая нужна, определение путей выборки для оптимизации для N + 1 и т.д.).

Эта автоматическая идея оптимизации запросов ORM вышла из Техасского университета (используя Hibernate). Он был включен в состав Ebean ORM в 2008 году, так что это было уже некоторое время.

3. Идентичность

Тед разбивается о несоответствии идентичности и concurrency. Этот момент неуместен как ORM (ну, все те, что я знаю) обходит этот аспект в точно той же усадьбе, что и предыдущие инструменты клиент/сервер, и, в частности, ORM предоставляют SNAPSHOT вид части базы данных в приложение. Здесь никогда не возникало проблемы, но реализация ORM могла попасть в конфликт с чрезмерной зависимостью от hashCode()/equals(), например.

4. Проблема с двойной схемой

Это спорно. Если организация разрешает, тогда ORM может предоставить схему DIFF/SQL script и которая запускается FlywayDB/Liquibase и т.д. Если организации не допускают, что это может быть проблемой в некоторой степени.

5. DB Рефакторинг/Общая схема

Это спорно. Разработчики/нормализаторы БД утверждают, что дизайн БД должен доходить до 4NF, и это означает, что любой рефакторинг должен быть исключительно аддитивным (денормализация, добавление столбцов/таблиц) и не нарушать изменений. Люди, которые не верят в нормализацию, будут беспокоиться об общей схеме.

Ответ 7

Думаю, что да.

Я думаю, что последнее предложение является самым интересным из всего: "Я склонен ошибаться на стороне базы данных как модель, потому что я думаю, что объекты переоценены". Java, С++ и С#, безусловно, являются доминирующими языками, но функциональное программирование возвращается с F #, Scala и т.д.

Ответ 8

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

Я не думаю, что его сообщение учитывало возможность самой структуры (в данном случае AcitveRecord/Rails), определяющую базу данных И объектную модель, которая, насколько я могу судить, заставляет проблему пойти прочь.

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

Ответ 9

IMHO, Монадические подходы вроде Scala Slick и Quill в значительной степени обойти вышеупомянутую трясину, предлагая более надежные решения для многих проблем Теда (также заслуживает упоминания JOOQ). Хотя они не идеальны, они определенно убивают проблемы с N + 1 и Partial Object, в основном убивают проблему Identity и частично убивают проблему Dual Schema. Забудьте о неуклюжих и подробных запросах CriteriaBuilder (кто-нибудь читает "Выполнение в Королевстве существительных???" ), Scala monadic for-comprehensions дает вам простой DSL, в котором нужно писать критерии-запросы:

case class Person(id: Int, name: String, age: Int)
case class Contact(personId: Int, phone: String)

val query = for {
    p <- query[Person] if(p.id == 999)
    c <- query[Contact] if(c.personId == p.id)
  } yield {
    (p.name, c.phone)
  }

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

case class Person(name:String, spouse:Option[PersonId]) {
    isThisAnAccpetableSpouse(person:Person) {...}
}

val query = for {
    p1 <- people
    p2 <- people if (
        p1.spouse.isEmpty && p2.spouse.isEmpty 
        && p1.isThisAnAccpetableSpouse(p2)
        && p1.isThisAnAccpetableSpouse(p1))
} yield (p1, p2)

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

Ответ 10

Раскрытие информации: я являюсь автором RDO.Net.

Да. Я полагаю, что существующие ORM движутся в неправильном направлении - они пытаются отобразить реляционные данные в произвольные объекты, что, IMHO, просто невозможно. В мире ОО произвольный объект не подходит для сериализации/десериализации, поскольку каждый объект имеет ссылку на объект (адрес), которая является локальной для текущего процесса. Это нормально, когда вы имеете дело с одним объектом, вы столкнетесь с проблемами, когда столкнетесь со сложным графом объектов.

Надеемся, что эту проблему можно решить с помощью другого подхода, как в RDO.Net: вместо сопоставления реляционных данных в произвольные объекты, мы отображаем схему данных в виде богатых метаданных (Model, который содержит столбцы, первичные/внешние ключи, проверки и т.д.), а также доступ к конкретным Db, DbTable, DbQuery и DataSet для доступа к данным. Выполняя операции CRUD с базой данных, вы создаете дерево абстрактного синтаксиса SQL (AST) явно, используя строго типизированный язык OO, как показано в следующем примере кода С#, который создает запрос и возвращает DataSet для иерархических данных:

public async Task<DataSet<SalesOrderInfo>> GetSalesOrderInfoAsync(_Int32 salesOrderID, CancellationToken ct = default(CancellationToken))
{
    var result = CreateQuery((DbQueryBuilder builder, SalesOrderInfo _) =>
    {
        builder.From(SalesOrderHeader, out var o)
            .LeftJoin(Customer, o.FK_Customer, out var c)
            .LeftJoin(Address, o.FK_ShipToAddress, out var shipTo)
            .LeftJoin(Address, o.FK_BillToAddress, out var billTo)
            .AutoSelect()
            .AutoSelect(c, _.Customer)
            .AutoSelect(shipTo, _.ShipToAddress)
            .AutoSelect(billTo, _.BillToAddress)
            .Where(o.SalesOrderID == salesOrderID);
    });

    await result.CreateChildAsync(_ => _.SalesOrderDetails, (DbQueryBuilder builder, SalesOrderInfoDetail _) =>
    {
        builder.From(SalesOrderDetail, out var d)
            .LeftJoin(Product, d.FK_Product, out var p)
            .AutoSelect()
            .AutoSelect(p, _.Product)
            .OrderBy(d.SalesOrderDetailID);
    }, ct);

    return await result.ToDataSetAsync(ct);
}