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

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

有人有任何有趣的意见或见解吗?行业现状如何?

有帮助吗?

解决方案

扩展关系数据库的难题是扩展事务,数据类型不匹配,自动查询转换以及类似 N + 1选择这是离开关系系统的基本问题,而且 - 在我看来 - 不要通过改变接收编程范式来改变。

其他提示

ORM 的目的是什么?

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

考虑到这种简单性,我相信 不存在“阻抗不匹配”这样的事情 两个模型之间。人们通常遇到的问题纯粹是特定于实现的,如果客户端和服务器之间有更好的数据传输协议,应该可以解决。

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

特别是, 第三宣言 试图通过允许嵌套集合来解决 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 中使用的示例 乔奥Q 从这篇博文 可能:

// 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)));
}

与通过某些 ORM 抽象 SQL 语言或使用 SQL 自然的“基于字符串”性质相比,这种方法可以使 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(), , ETC。

回答你的问题

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

(我在后面的公司工作 乔奥Q, ,所以这个答案有偏见)

这取决于你的需求

  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 会话的约束,并且大部分时间都依赖于 SQL 上的 DSL(因此可移植性无关紧要) 但另一方面,你必须查看交易细节,并发问题

List<Person> persons = queryFactory.selectFrom(person)
  .where(
    person.firstName.eq("John"),
    person.lastName.eq("Doe"))
  .fetch();

我认为关系映射的功能应该比OO到RDBMS更容易创建和使用。只要你只查询数据库,那就是。我还没有真正看到(你)如何以一种很好的方式进行没有副作用的数据库更新。

我看到的主要问题是表现。今天的RDMS并不是设计用于功能查询,并且在很多情况下可能表现不佳。

我还没有完成功能关系映射,本身,但我使用了函数式编程技术来加速对RDBMS的访问。

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

似乎合理。但问题是它可能变得非常慢。如果您的计算需要除更新查询本身之外的其他SQL语句,或者甚至需要在应用程序代码中完成,那么您必须(重新)搜索计算后要更改的记录以将结果存储在正确的行中

您只需为结果创建一个新表即可解决此问题。这样,您可以随时插入而不是更新。你最终得到另一个表,复制键,但你不再需要在存储NULL的列上浪费空间<!>#8211;你只存储你拥有的东西。然后,您将结果加入到最终选择中。

我(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的语言的语言。

我想这也可以让并行更容易。

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

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

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

功能<!> lt; - <!> gt; RDBMS映射应该与OO <!> lt; - <!> gt; RDMBS映射没有太大区别。但我认为这很大程度上取决于您要使用哪种数据类型,如果您想要使用全新的数据库架构开发程序或者针对遗留数据库架构执行某些操作等等。

例如,对于关联的延迟提取等可能与一些懒惰的评估相关概念很好地实现。 (即使他们也可以用OO做得很好)

编辑:通过一些谷歌搜索,我找到了 HaskellDB (SQL库, Haskell) - 值得一试吗?

数据库和功能编程可以融合。

例如:

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中,规范更像是RMDB。 请参阅: spec-alpha2 wiki:Schema-and-select

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

鸭子打字:如果它看起来像鸭子,像鸭子一样嘎嘎叫,它一定是鸭子。

如果clojure的数据模型像RMDB,clojure的设施如RMDB和clojure的数据操作(如RMDB),clojure必须是RMDB。

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

一切都是RMDB

基于哈希映射实现关系数据模型和编程(NoSQL) )

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top