【问题标题】:Is ORM still the "Vietnam of Computer Science"? [closed]ORM 还是“计算机科学的越南”吗? [关闭]
【发布时间】:2010-09-29 01:53:51
【问题描述】:

我昨晚读了this post,我注意到它是从 2006 年开始的。我可以在 ORM、数据库方面采取任何一种方式,但我只是想知道 Jeff 所说的关于 ORM 的所有坏话是否仍然适用,即使现在考虑到这篇文章是从 2006 年开始的。

【问题讨论】:

  • 在 2004 年撰写这篇文章时,越南是美国的最后一个参考点。现在说“计算机科学的伊拉克”不是很安全吗?或者再过二十年还是不酷?
  • @Daniel:是的,太快了。越南仍然是“顽固的泥潭”的首选隐喻。
  • 我会说 CS 的“阿富汗”。它提供了更丰富多彩的比较。

标签: database language-agnostic orm


【解决方案1】:

披露:我是RDO.Net的作者。

是的。我相信现有的 ORM 正朝着错误的方向发展——他们试图将关系数据映射到任意对象中,恕我直言,这是不可能的。在 OO 世界中,任意对象不是序列化/反序列化友好的,因为每个对象都有一个当前进程本地的对象引用(地址)。处理单个对象没问题,遇到复杂对象图就麻烦了。

希望这个问题可以使用不同的方法来解决,如RDO.Net:我们不是将关系数据映射到任意对象,而是将数据的模式映射为丰富的元数据(Model 包含列,主要/外部密钥、验证等),并公开具体的DbDbTableDbQueryDataSet 以进行数据访问。执行数据库 CRUD 操作时,您可以使用强类型 OO 语言显式构建 SQL 抽象语法树 (AST),如以下示例 C# 代码所示,该代码创建查询并返回分层数据的 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);
}

【讨论】:

    【解决方案2】:

    恕我直言,像 Scala 的 SlickQuill 这样的 Monadic 方法在很大程度上绕过了上述泥潭,为 Ted 的许多问题提供了更强大的解决方案(JOOQ 也值得一提)。虽然它们并不完美,但它们肯定会解决 N+1 和 Partial Object 问题,主要解决 Identity 问题,部分解决 Dual Schema 问题。忘掉笨拙和冗长的 CriteriaBuilder 查询(任何人都读过“名词王国中的执行???”),Scala 的一元 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)
      }
    

    这种语法保持理智,即使对于更复杂的查询,例如Ted 的是否可接受配偶查询:

    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 查询并针对数据库运行。插入、更新和删除是类似的。

    【讨论】:

    • 这些更好地称为 FRM 而不是 ORM。函数式编程与面向对象编程没有相同的范式差异……事实上,关系范式是函数式范式的一个子集。因此,为什么 FRM 完全避免了 ORM 的问题。老实说,唯一真正的 ORM 还算不错,它使用函数机制来实现一些奇偶校验。
    【解决方案3】:

    Jeffs 文章链接到 Ted Newards 文章。如果您了解细节,那么您需要查看那里:

    1. 原创 - http://blogs.tedneward.com/post/the-vietnam-of-computer-science/

    2. 跟进 - http://blogs.tedneward.com/post/thoughts-on-vietnam-commentary/

    Ted 的原点我有:

    • 1 错误(身份)
    • 其中 2 个已解决(部分对象和 N + 1)
    • 2 是有争议的(双模式/共享模式)。

    免责声明:我是 Ebean ORM 的作者,因此我将参考它来解决所提出问题的各种“解决方案”。

    Ted 的原点(因为实在是罗嗦所以提炼出来):

    1.部分对象问题。

    总是可以解决的。 Ebean ORM 使部分对象成为其查询语言和所有内部结构的基础。不幸的是,JPQL 并没有将此作为优先事项。

    2。 N + 1(Ted 的加载时间悖论)

    总是可以解决的。应该写成1 + N / batchSize,但它比这更有趣(每个路径,需要考虑SQL分页,避免sql笛卡尔积)。不幸的是,一些 ORM 把这件事弄得一团糟,这使 ORM 总体上声名狼藉。一些 ORM 是可以的,直到您达到一定程度的复杂性(例如 OneToMany 中的 OneToMany 中的 OneToMany)。

    为了提高赌注,ORM 可以分析对象图的使用并自动优化查询(只获取需要的内容,定义获取路径以优化 N + 1 等)。

    这种自动 ORM 查询优化的想法来自德克萨斯大学(使用 Hibernate)。它在 2008 年作为 Ebean ORM 的一部分包含在内,所以它已经存在了一段时间。

    3.身份

    Ted 破解了身份和并发性不匹配的问题。这一点是错误的,因为 ORM(好吧,我所知道的所有)在这方面完全与以前的客户端/服务器工具相同,特别是 ORM 提供 SNAPSHOT strong> 向应用程序查看部分数据库。这里从来没有问题,但 ORM 实现可能会因过度依赖 hashCode()/equals() 而陷入冲突。

    4.双模式问题

    这是有争议的。如果组织允许,那么 ORM 可以向架构提供 DIFF/SQL 脚本,该脚本由 FlywayDB/Liquibase 等运行。如果组织不允许,这在某种程度上可能仍然是一个问题。

    5.数据库重构/共享架构

    这是有争议的。 DB 设计/规范化人员会争辩说,DB 设计应该达到 4NF,这意味着任何重构都应该只是附加的(非规范化、添加列/表)而不是破坏性更改。不相信规范化的人会因为担心共享模式而发疯。

    【讨论】:

      【解决方案4】:

      许多 web 2.0 公司都在致力于键值存储。而且所有这些公司都必须经历同样痛苦的过程才能使其运作。

      如果 ORM 是“计算机科学的越南”,那么构建自己的键值存储可能就是“计算机科学的伊拉克”:-)

      【讨论】:

        【解决方案5】:

        我认为从 Jeff 的结论正确的假设出发并不一定是好的;在维护了存储过程代码以及基于 JDBC 的数据层之后,我可以说这些都会导致大量的维护问题,主要与无法理解更高级别发生的事情有关。

        数据库必然是低级的;它本质上存储数字和字符串。业务逻辑是高级别的。这就是我们有抽象的原因。

        就我个人而言,我认为 Rails/ActiveRecord 方式是拥有对象/域模型但也能够利用关系数据库的最佳解决方案。

        所以:不要丢弃 ORM,但也不要默认使用它。它是解决某些问题的工具。忽视它是无知的,总是使用它是傲慢的。

        【讨论】:

          【解决方案6】:

          是的。

          面向对象仍然是面向对象,而关系仍然是面向集合。在过去的两年中,这两种范式没有发生任何变化,以使它们更好地协同工作。

          在许多人眼中,SQL 丑陋、复杂且令人困惑。但是试图创建一个面向对象的接口来执行相同的功能总是更丑陋、更复杂,并且学习曲线更陡峭。

          在所有编程中,灵活性假设之间都有一个折衷。框架(例如 Rails)试图通过“固执己见”来解决问题。也就是说,它们限制了问题的关系或面向对象方面的灵活性,对数据的结构方式以及您可以对其进行哪些操作进行假设。当然,简化问题空间也会使解决方案更简单。

          此外,发现一个ORM框架是不完整的,令人沮丧,因此SQL中的一些普通操作在给定的ORM中没有解决方案。这也是“固执”框架的结果。

          【讨论】:

          • “自然,简化问题空间也使解决方案更简单。” - Bill Karwin Gee Bill,谢谢,我对那句话感到安慰。 :)
          • 但是等一下,像 Hibernate 这样的框架不支持记录集类型的东西吗(即投影查询?)我现在可能会得到的答复是,什么是考虑到开销,当您有一个像 SQL 一样运行的投影查询时,使用框架的意义。
          • 根据 ohloh.net,Hibernate 包含超过 800,000 行 Java 和 XML 代码(不包括 cmets 或其他与 Hibernate 相关的工具)。考虑到它与 SQL 一样难以使用并且并不能真正更好地解决问题,这种开销真的有必要吗?
          【解决方案7】:

          我只能代表我的经验。我使用 DAL 和 DTO,我仍然能够执行非常复杂的查询(连接和所有),并且我可以在需要时使用 SP 或自定义 SQL。它让我的生活更轻松,我的代码更一致,我的最后期限更容易实现。

          【讨论】:

          • 仅仅因为你能做到,并不意味着你应该这样做。您最终学习了另一种范式,增加了复杂性,而没有替换其他两个范式。
          • @le dorfier:我 8 年多来使用这种方法的许多成功项目确实是我需要的唯一验证,我只是想提供一个不同的视角。我不认为我增加了复杂性,但理性的人可能会不同意。
          • 是的,我认为 RoR 是一个有用的框架。我希望它可以拥有它所设计的 isam,而不是与 RDBMS 不匹配。然后您可以使用它来优化持久性,以获得净复杂度增益。 (有时复杂性是值得的;但假装它不存在也无济于事。)
          • 我不会使用 RoR,甚至不会使用 Hibernate,它们根本无法扩展。相信我,我煞费苦心地分析,使它成为线程安全的,我可以用它运行繁重的进程 - 无需手动编写一行 SQL。
          • 我不会试图暗示它不能完成——它显然已经完成了。但是,恕我直言,数据库最好在数据库中完成。并且线程安全根本没有出现。顺便说一句,我赞成你。恕我直言,您的模型比 ORM 更可行。
          【解决方案8】:

          这仍然是真的。

          与 OO 软件相比,如果没有按照预期的方式精确处理数据库,数据库也会受到影响。并且不打算在它前面插入一些抽象层。

          我认为不透水的抽象层就像试图建造一座乐高城堡,所有的零件都封闭在一个枕套中。 SQL 很难正确执行。它与过程式编程没有很多相同的模式,一个最佳实践可能与另一个相反。您需要能够理解 SQL 语句中的每一个项目,并且非常清楚它的意图是什么,以及它实际上做了什么。

          很多人似乎认为,就像马蹄铁一样,接近就足够了 - 如果弹出正确的答案,那就意味着你快到了。在 SQL 中,这根本不是真的。

          出于这个原因,RoR 和 ActiveRecord 模式当之无愧地赢得了 dbms 资源消耗者的美誉。优化的 ActiveRecord 设计往往不是次优的 SQL 设计,因为它鼓励 SQL 语句分解。

          【讨论】:

          • 实际上,SQL 很容易做到。如果您知道自己在做什么。其他所有技术都可以这么说,所以如果你认为它很难,你不应该尝试使用 ORM 来强制它,你只需要与 DBA 交朋友(他们会认为过程编码不可能很难:))
          • “SQL 很难正确执行。它与过程编程没有很多共同点”:呃,这不正是 ORM 的意义所在吗?即对于出于某种原因想要避免参与 SQL 的过程程序员?我希望权衡是已知的并被认为是可以接受的。
          • 我知道 SQL,是的,我真的不认为它那么难......(虽然很难学习......一旦你掌握了它,它就不会那么糟糕)
          • 很少有调试和维护问题比查看一些小丑的链接在一起的 lambda 表达式(包括连接、分组等)更糟糕。与调试存储过程甚至参数化查询相比,它是一个彻头彻尾的噩梦。
          • 是的 - 除非您知道 SQL,否则您不是“开发人员”
          【解决方案9】:

          这不是我的专业领域,但我在 Rails 工作了大约一年,我认为 ActiveRecord 解决了大部分数据库映射问题。我意识到它存在一些问题,但我认为它做得非常好。

          我认为他的帖子没有考虑到框架本身(在本例中为 AcitveRecord/Rails)定义数据库和对象模型的可能性,据我所知,这使问题解决了离开。

          由于这与前两个回复相反(基本上是帖子已过时),我觉得我可能不理解某些东西;如果是这种情况,请纠正我,而不是仅仅投反对票,因为我认为我遗漏了重要的一点。

          【讨论】:

          • 它不会让问题消失,它会产生问题得到解决的错觉,但与使用数据库和 SQL 的全部功能相比,会产生可怕的性能和设计限制。纯对象数据库(本质上就是您所说的)从未流行起来。
          • ++ 肯德尔。 ActiveRecord 的设计自然会导致可怕的性能瓶颈(非常易于使用的抽象层的缺点)。
          • ActiveRecord(模式,而不是 Rails 实现)如果您在设计的环境中使用它就可以了。但是 Martin Fowler 的 PoEAA 书中还有一堆其他的数据访问模式,似乎被 Rails 忽略了!
          • 有趣的点,谢谢。性能通常在数据库和 Web 应用程序中至关重要。我不确定 ActiveRecord 类型的实现是否会牺牲性能,但从报告 rails 实现问题来看,我猜目前它不太好。
          • 是的,等一下……我怀疑活动记录是 Rails 的问题。众所周知,Ruby 本身很慢(1.9 有很大帮助),Rails 也很慢。
          【解决方案10】:

          我认为是的。

          我认为最后一句话是最有趣的:“我倾向于选择数据库模型阵营,因为我认为对象被高估了。” Java、C++ 和 C# 无疑是主要语言,但函数式编程正在与 F#、Scala 等一起卷土重来。

          【讨论】:

          • 卷土重来 - 他们以前从未成为主导模型,因此“突破”可能是正确的:D
          猜你喜欢
          • 2013-01-29
          • 2010-09-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-12-06
          • 1970-01-01
          相关资源
          最近更新 更多