WW.*_*WW. 17 database orm rdbms functional-programming
已经很好地讨论了对象关系映射,包括在这里.我有一些方法的经验,陷阱和妥协.真正的分辨率似乎需要更改OO或关系模型本身.
如果使用功能语言,是否会出现同样的问题?在我看来,这两种范式应该比OO和RDBMS更好地融合在一起.在RDBMS中进行思考的想法似乎与功能方法似乎承诺的自动并行性相匹配.
有没有人有任何有趣的意见或见解?这个行业的状况如何?
Luk*_*der 11
使用ORM的主要目的是在网络模型(面向对象,图形等)和关系模型之间建立桥梁.两种模型之间的主要区别非常简单.父母是指儿童(网络模型)还是儿童指向父母(关系模型).
考虑到这种简单性,我相信两种型号之间不存在"阻抗不匹配"的问题.人们通常遇到的问题纯粹是特定于实现的,如果客户端和服务器之间有更好的数据传输协议,则应该是可以解决的.
特别是,第三个宣言试图通过允许嵌套集合来解决SQL语言和关系代数的缺点,这些集合已经在各种数据库中实现,包括:
在我看来,如果所有数据库都实现了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
Run Code Online (Sandbox Code Playgroud)
将所有演员和他们的电影作为嵌套集合,而不是非规范化的连接结果(每个电影重复演员).
客户端的函数式编程语言是否更适合数据库交互的问题实际上是正交的.ORM有助于对象图持久性,因此如果您的客户端模型是图形,并且您希望它是图形,那么无论您是使用函数式编程语言操作该图形,都需要ORM.
但是,由于面向对象在函数式编程语言中不那么惯用,因此您不太可能将每个数据项都塞进对象中.对于编写SQL的人来说,投射任意元组是非常自然的.SQL包含结构类型.每个SQL查询都定义自己的行类型,而无需事先为其指定名称.这与函数式程序员非常吻合,特别是在类型推断很复杂的情况下,如果你不想到将SQL结果映射到一些先前定义的对象/类.
// 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)));
}
Run Code Online (Sandbox Code Playgroud)
与SQL语言被某些ORM抽象,或者使用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);
Run Code Online (Sandbox Code Playgroud)
一些FRM尝试通过SQL语言进行抽象,通常是出于以下原因:
JOIN
被翻译成flatMap()
并被WHERE
翻译成filter()
等等.FRM并不比ORM"更容易",它解决了一个不同的问题.实际上,FRM并没有真正解决任何问题,因为SQL本身就是一种声明性编程语言(与函数式编程没有太大区别),它非常适合其他功能性客户端编程语言.因此,如果有的话,FRM只是填补了SQL,外部DSL和客户端语言之间的空白.
(我为jOOQ背后的公司工作,所以这个答案有偏见)
扩展关系数据库的难题是扩展事务,数据类型不匹配,自动查询转换以及N + 1选择这些是离开关系系统的基本问题 - 在我看来 - 不要通过更改接受编程范式.
那要看你的需要
各种“关系映射”技术的强项是可移植性:您确保您的应用程序将在大多数ACID数据库上运行。否则,当您手动编写SQL请求时,您将解决各种SQL方言之间的差异。
当然,您可以约束自己使用SQL92标准(然后执行一些函数式编程),也可以在ORM框架中重用一些函数式编程的概念。
ORM优势建立在一个会话对象之上,该对象可能会成为瓶颈:
但是,它的优点也是缺点:
会话必须能够比较对象,因此您需要实现equals / hashCode方法。但是对象相等性必须植根于“业务键”而不是数据库ID(新的瞬态对象没有数据库ID!)。但是,某些概念化的概念没有业务平等性(例如,一个操作)。常见的解决方法是依靠GUID,这会使数据库管理员感到不安。
会话必须监视关系的更改,但是其映射规则会强制使用不适合业务算法的集合。有时您想使用HashMap,但ORM将要求密钥成为另一个“ Rich Domain Object”而不是另一个轻量级的...然后,您必须在充当密钥的Rich Domain对象上实现对象相等性。但是您不能,因为该对象在商业世界中没有对应对象。因此,您可以使用一个简单的列表进行迭代(并由此导致性能问题)。
ORM API有时不适合实际使用。例如,现实世界中的Web应用程序尝试通过在获取数据时添加一些“ WHERE”子句来强制会话隔离。然后,“ Session.get(id)”就不够用了,您需要转向更复杂的DSL( HSQL,标准API)或返回到本机SQL
数据库对象与专用于其他框架的其他对象(例如OXM框架=对象/ XML映射)冲突。例如,如果您的REST服务使用杰克逊库来序列化业务对象。但这名杰克逊正好映射到一个“冬眠者”。然后要么合并两者,要么在API和数据库之间出现牢固的耦合,或者您必须实施转换,从ORM保存的所有代码都在那里丢失了...
另一方面,FRM是“对象关系映射”(ORM)和本机SQL查询(使用JDBC)之间的折衷方案
解释FRM和ORM之间差异的最好方法是采用DDD方法。
它释放了对ORM会话的约束,并且大部分时间都依赖SQL上的DSL(因此可移植性无关紧要)。但是,另一方面,您必须研究事务详细信息,并发问题
List<Person> persons = queryFactory.selectFrom(person)
.where(
person.firstName.eq("John"),
person.lastName.eq("Doe"))
.fetch();
Run Code Online (Sandbox Code Playgroud)