代码之家  ›  专栏  ›  技术社区  ›  WW.

函数到关系映射比对象到关系映射更容易吗?[关闭]

  •  20
  • WW.  · 技术社区  · 17 年前

    对象关系映射已经得到了很好的讨论,包括在这里。我有一些方法以及陷阱和妥协的经验。真正的解决方案似乎需要对OO或关系模型本身进行更改。

    如果使用函数式语言,是否也会出现同样的问题?在我看来,这两种范式应该比OO和RDBMS更好地结合在一起。在RDBMS中以集合思维的想法似乎与函数式方法所承诺的自动并行性相吻合。

    有人有什么有趣的观点或见解吗?该行业的发展状况如何?

    8 回复  |  直到 10 年前
        1
  •  17
  •   Lukas Eder    7 年前

    ORM的目的是什么?

    使用ORM的主要目的是在网络模型(面向对象、图形等)和关系模型之间架起桥梁。这两种模型之间的主要区别非常简单。这取决于父母是指向孩子(网络模型)还是孩子指向父母(关系模型)。

    考虑到这种简单性,我相信 there is no such thing as an "impedance mismatch" 在两个模型之间。人们通常遇到的问题纯粹是特定于实现的,如果客户端和服务器之间有更好的数据传输协议,这些问题应该是可以解决的。

    SQL如何解决我们在ORM中遇到的问题?

    特别是 third manifesto 试图通过允许嵌套集合来解决SQL语言和关系代数的缺点,嵌套集合已在各种数据库中实现,包括:

    • Oracle(可能是最复杂的实现)
    • PostgreSQL(在某种程度上)
    • 数据库
    • SQL Server、MySQL等。(通过XML或JSON的“模拟”)

    在我看来,如果所有数据库都实现了SQL标准 MULTISET() 运算符(例如Oracle),人们将不再使用ORM进行映射(可能仍然用于对象图持久化),因为它们可以直接从数据库中实现嵌套集合,例如以下查询:

    SELECT actor_id, first_name, last_name,
      MULTISET (
        SELECT film_id, title
        FROM film AS f
        JOIN film_actor AS fa USING (film_id)
        WHERE fa.actor_id = a.actor_id
      ) AS films
    FROM actor AS a
    

    将所有演员及其电影作为嵌套集合生成,而不是非标准化的连接结果(每部电影都重复演员)。

    客户端的功能范式

    客户端的函数式编程语言是否更适合数据库交互的问题实际上是正交的。ORM有助于对象图持久化,因此,如果您的客户端模型是一个图,并且您希望它是一个图形,那么无论您是否使用函数式编程语言操纵该图,都需要一个ORM。

    然而,由于面向对象在函数式编程语言中不太习惯,您不太可能将每个数据项硬塞进一个对象中。对于编写SQL的人来说,投影任意 元组 这很自然。SQL支持结构类型。每个SQL查询都定义了自己的行类型,而无需事先为其指定名称。这与函数式程序员非常契合,尤其是在类型推理很复杂的情况下,在这种情况下,你永远不会想到将SQL结果映射到一些以前定义的对象/类。

    Java中使用的示例 jOOQ from this blog post 可以是:

    // Higher order, SQL query producing function:
    public static ResultQuery<Record2<String, String>> actors(Function<Actor, Condition> p) {
        return ctx.select(ACTOR.FIRST_NAME, ACTOR.LAST_NAME)
                  .from(ACTOR)
                  .where(p.apply(ACTOR)));
    }
    

    这种方法导致SQL语句的组合性比SQL语言被一些ORM抽象或使用SQL的自然“基于字符串”的性质要好得多。现在可以使用上述函数,例如:

    // Get only actors whose first name starts with "A"
    for (Record rec : actors(a -> a.FIRST_NAME.like("A%")))
        System.out.println(rec);
    

    SQL上的FRM抽象

    一些FRM试图抽象SQL语言,通常出于以下原因:

    • 他们声称SQL不够可组合(jOOQ反驳了这一点,只是很难做到正确)。
    • 他们声称API用户更习惯于“原生”集合API,因此例如。 JOIN 被翻译成 flatMap() WHERE 被翻译成 filter() 等等。

    回答你的问题

    FRM并不比ORM“容易”,它解决了一个不同的问题。事实上,FRM根本不能解决任何问题,因为SQL本身是一种声明性编程语言(与函数式编程没有太大区别),与其他函数式客户端编程语言非常匹配。因此,如果有的话,FRM只是弥合了SQL、外部DSL和客户端语言之间的差距。

    (我为后面的公司工作 jOOQ ,所以这个答案是有偏见的)

        2
  •  10
  •   David Schmitt    17 年前

    扩展关系数据库的难题是扩展事务、数据类型不匹配、自动查询转换等 N+1 Select 这是离开关系系统的基本问题,在我看来,改变接收编程范式不会改变这些问题。

        3
  •  7
  •   jmlamare    10 年前

    这取决于你的需求

    1. 如果你想专注于数据结构,可以使用类似JPA/Hibernate的ORM
    2. 如果你想了解治疗方法,请查看FRM库:QueryDSL或Jooq
    3. 如果需要将SQL请求调整到特定数据库,请使用JDBC和本机SQL请求

    各种“关系映射”技术的优点是可移植性:您可以确保您的应用程序在大多数ACID数据库上运行。 否则,在手动编写SQL请求时,您将应对各种SQL方言之间的差异。

    当然,您可以限制自己使用SQL92标准(然后进行一些函数式编程),或者您可以使用ORM框架重用一些函数式程序设计的概念

    ORM的强度是建立在会话对象之上的,会话对象可能会成为瓶颈:

    1. 只要底层数据库事务正在运行,它就会管理对象的生命周期。
    2. 它在java对象和数据库行之间保持一对一的映射(并使用内部缓存来避免重复对象)。
    3. 它会自动检测关联更新和要删除的孤立对象
    4. 它处理乐观或悲观锁的并发问题。

    然而,它的优点也是缺点:

    1. 会话必须能够比较对象,因此您需要实现equals/hashCode方法。 但是对象相等性必须基于“业务密钥”,而不是数据库id(新的瞬态对象没有数据库id!)。 然而,一些具体化的概念没有业务相等性(例如操作)。 一种常见的解决方法依赖于GUID,这往往会让数据库管理员感到不安。

    2. 会话必须监视关系更改,但其映射规则会推动使用不适合业务算法的集合。 有时你想使用HashMap,但ORM要求密钥是另一个“富域对象”,而不是另一个轻量级对象。.. 然后,您必须在充当密钥的富域对象上实现对象相等。.. 但你不能,因为这个对象在商业世界中没有对应物。 因此,你回到了一个必须迭代的简单列表(性能问题由此产生)。

    3. ORM API有时不适合在现实世界中使用。 例如,现实世界的web应用程序试图通过在获取数据时添加一些“WHERE”子句来强制会话隔离。.. 然后“Session.get(id)”就不够了,您需要转向更复杂的DSL(HSQL,Criteria API)或返回到本机SQL

    4. 数据库对象与专用于其他框架的其他对象(如OXM框架=对象/XML映射)冲突。 例如,如果您的REST服务使用jackson库来序列化业务对象。 但这个Jackson正好映射到Hibernate One。 然后,要么合并两者,API和数据库之间就会出现强耦合 或者你必须实现一个翻译,你从ORM保存的所有代码都会在那里丢失。..

    另一方面,FRM是“对象关系映射”(ORM)和本机SQL查询(使用JDBC)之间的权衡

    解释FRM和ORM之间差异的最佳方法是采用DDD方法。

    • 对象关系映射允许使用“富域对象”,即在数据库事务期间状态可变的Java类
    • 函数关系映射依赖于“不良域对象”,这些对象是不可变的(以至于每次想要更改其内容时都必须克隆一个新的对象)

    它释放了对ORM会话的约束,并且大部分时间依赖于DSL而不是SQL(因此可移植性无关紧要) 但另一方面,您必须研究事务细节和并发问题

    List<Person> persons = queryFactory.selectFrom(person)
      .where(
        person.firstName.eq("John"),
        person.lastName.eq("Doe"))
      .fetch();
    
        4
  •  3
  •   Sam    17 年前

    我想函数到关系映射应该比OO到RDBMS更容易创建和使用。只要你只查询数据库,也就是说。我还不知道如何以一种没有副作用的方式进行数据库更新。

    我看到的主要问题是性能。今天的RDMS不是为函数式查询而设计的,在很多情况下可能表现不佳。

        5
  •  3
  •   Svante    14 年前

    我还没有做过函数关系映射, 本身 ,但我使用了函数式编程技术来加快对RDBMS的访问。

    从数据集开始,对其进行一些复杂的计算,并存储结果,这是很常见的,例如,结果是具有附加值的原始数据集的子集。命令式方法要求您用额外的NULL列存储初始数据集,进行计算,然后用计算值更新记录。

    看起来很合理。但问题是,它可能会变得非常缓慢。如果你的计算除了更新查询本身之外还需要另一个SQL语句,或者甚至需要在应用程序代码中完成,那么你必须(重新)搜索计算后正在更改的记录,以便将结果存储在正确的行中。

    您可以通过简单地创建一个新的结果表来解决这个问题。这样,您就可以始终插入而不是更新。你最终得到了另一个表,复制了键,但你不再需要在存储NULL的列上浪费空间——你只存储你所拥有的。然后,您将结果加入最终选择中。

    我(ab)以这种方式使用RDBMS,最终编写的SQL语句大多看起来像这样。..

    create table temp_foo_1 as select ...;
    create table temp_foo_2 as select ...;
    ...
    create table foo_results as
      select * from temp_foo_n inner join temp_foo_1 ... inner join temp_foo_2 ...;
    

    这本质上是在创建一堆不可变的绑定。不过,好消息是,你可以一次完成整套工作。这让你想起了可以使用矩阵的语言,比如Matlab。

    我想这也会让并行性变得容易得多。

    一个额外的好处是,以这种方式创建的表的列类型不必指定,因为它们是从所选的列中推断出来的。

        6
  •  3
  •   Lukas Eder    7 年前

    我认为,正如Sam提到的,如果数据库应该更新,那么必须面对与面向对象世界相同的并发问题。由于RDBMS的数据状态、事务等,程序的功能性质可能比对象性质更成问题。

    但对于阅读来说,函数式语言在某些问题领域可能更自然(似乎与数据库无关)

    功能性<->RDBMS映射应该与OO没有太大区别<->RDMBS映射。但我认为这在很大程度上取决于你想使用哪种数据类型,如果你想用一个全新的数据库模式开发一个程序,或者对传统的数据库模式做点什么,等等。。

    例如,关联的懒惰获取等可能可以很好地与一些懒惰评估相关的概念一起实现。(尽管它们也可以用OO很好地完成)

    编辑:我用谷歌搜索了一下 HaskellDB (Haskell的SQL库)-这值得一试吗?

        7
  •  1
  •   Lin Pengcheng    7 年前

    数据库和函数式编程可以融合。

    例如:

    Clojure是一种基于关系数据库理论的函数式编程语言。

                   Clojure -> DBMS, Super Foxpro
                       STM -> Transaction,MVCC
    Persistent Collections -> db, table, col
                  hash-map -> indexed data
                     Watch -> trigger, log
                      Spec -> constraint
                  Core API -> SQL, Built-in function
                  function -> Stored Procedure
                 Meta Data -> System Table
    
    

    注意:在最新的spec2中,spec更像RMDB。 参见: spec-alpha2 wiki: Schema-and-select

    我主张:在哈希映射之上构建关系数据模型,以实现NoSQL和RMDB优势的结合。这实际上是posgtresql的反向实现。

    鸭子打字:如果它看起来像鸭子,叫声也像鸭子,那它一定是鸭子。

    如果clojure的数据模型像RMDB,clojure设施像RMDB并且clojure数据操作像RMDB的话,clojule必须是RMDB。

    Clojure is a functional programming language based on relational database theory

    Everything is RMDB

    Implement relational data model and programming based on hash-map (NoSQL)

    推荐文章