title: 对象关系映射器 (ORMs) category: page slug: object-relational-mappers-orms sortorder: 0504 toc: False sidebartitle: 对象关系映射器 meta: 对象关系映射器 (ORMs) 在关系型数据库和面向对象的代码间起了连接作用。到 Full Stack Python 上学习更多相关知识。 updated: 2016-07-17 writing-date: 2016-07-13 19:26—2016-07-17 13:20

对象关系映射器 (ORM)

对象关系映射器 (ORM) 是一种代码库,它能自动将存储在关系型数据库表中的数据转化成在应用程序代码中更加常用的对象。

为什么 ORM 很有用?

ORM 为 关系型数据库 提供了高级的抽象,它使得开发人员不必写 SQL,只需写 Python 代码就能在数据库中创建、读取、更新和删除数据。开发人员能使用他们熟悉的编程语言来处理数据库,而无需编写 SQL 语句或存储过程。

例如,如果没有 ORM 的话,开发人员需要编写以下的 SQL 语句来读取 USERS 表中的 zip_code 列的值为 94107 的所有行:

  1. SELECT * FROM USERS WHERE zip_code=94107;

而其相应的 Django ORM 查询语句看起来会如下面的 Python 代码:

  1. # obtain everyone in the 94107 zip code and assign to users variable
  2. users = Users.objects.filter(zip_code=94107)

编写 Python 代码来代替编写 SQL,能提高 Web 应用的开发速度,特别是在项目前期的时候。开发速度的提升潜力源于无需从编写 Python 代码切换到编写声明式的 SQL 语句。虽然有些开发人员可能不太介意在两种语言间来回切换,但是使用一种编程语言对于捣鼓出一个原型或者开始创建一个 Web 应用通常更加容易。

从理论上来说,ORM 也使得应用程序在不同的关系型数据库之间切换使用变得可能。比如说,开发人员可以在本地开发中使用 SQLite,然后在生产环境中使用 MySQL。 只需进行少量的代码修改,生产环境中的应用就能实现从 MySQL 切换到 PostgreSQL

但在实践中,本地开发环境和生产环境最好使用相同的数据库。不然的话,在本地开发环境从未碰到的一些始料未及的错误可能会出现在生产环境中。而且,对于一个项目来说,很少有需将生产环境中的数据库切换到另一个数据库的情况,除非有很紧急的原由。

学习 ORM 的时候,你也应该学习下 部署应用依赖 章节。

我需要在我的 Web 应用使用 ORM 吗?

Python ORM 库对于存取关系型数据库来说不是必须的。实现上,低层的存取接口通常是由另一个叫 数据库连接器 的库来提供的,例如 psycopg (用于 PostgreSQL) 或 MySQL-python (用于 MySQL) 等。 下表列出了 ORM 与不同的 Web 框架、连接器及关系型 数据库一起使用的情况。

各种 Python ORM 如何能与不同的连接器及后端一起使用的例子。

从上面的表格中可以看出,例如,SQLAlchemy 能与不同的 Web 框架和数据库连接器一起使用。开发人员也可以撇开 Web 框架单独使用 ORM,比如在创建一个数据分析工具或者没有用户界面的批处理脚本时。

使用 ORM 有什么缺点?

ORM 有很多的缺点,包括:

  1. 阻抗失配
  2. 有可能降低性能
  3. 将复杂性从数据库转移到了应用程序代码中

阻抗失配

“阻抗失配” 这个术语通常和 ORM 一起使用。阻抗失配这个术语,是对于数据在关系型数据表与应用程序对象之间变换时的所有难度的总称。其要点是:开发人员使用对象的方式与数据在关系型数据表中如何被存储和关联是有差异的。

这篇关于 ORM 阻抗失配的文章 对该概念进行了细致的综述,并提供图表以可视化方式解析了该问题发生的原因。

降低性能的潜在可能

使用任何一个高级抽象或框架,存在的其中一个顾虑是有存在降低性能的潜在可能性。使用 ORM 时,性能影响因素源自:应用代码会被转化成没有被合理调优过的 SQL 语句。

ORM 通常是上手容易但真正掌握很难。比如说,一个 Django 新手无需知道 select_related() function 及其如何能提升某些外键关联型查询的性能等知识,就能使用 ORM 了。每种 ORM 都有许多有关性能的提示和技巧。花时间学习这些技能比只学习 SQL 及如何编写存储过程可能有用的多。

这章里有许多显而易见的 “可能或可能不” 以及 ”潜在的“ 等字眼。在大型项目中,ORM 非常适用于大约 80-90% 的情况,而项目中剩下 10-20% 的数据库交互操作,可以由经验丰富的数据库管理员编写调优过的 SQL 语句来代码 ORM 生成的 SQL 代码,从而使其性能得到显著提高。

将复杂度从数据库转到应用程序代码

我们需要编写一些代码来处理应用的数据。在 ORM 之前,数据库存储过程被用于封装数据库逻辑。而在 ORM 中,操作数据的代码存在于应用的 Python 代码中。这些额外的数据操作逻辑,对于一个设计良好的应用来说,通常都不会成为一个问题,但因其没有在应用程序和数据库存储过程之间将代码进行拆分,从而提高了 Python 的总代码量。

Python ORM 的实现

存在很多个用 Python 编写的 ORM 实现,包括

  1. Django ORM
  2. SQLAlchemy
  3. Peewee
  4. PonyORM
  5. SQLObject

还有其他一些 ORM, 例如 Canonical 的 Storm,但是其中大多数现在看来都已经停止开发了。让我们进一步了解以下几个主要的还活跃着的 ORM 吧。

Django ORM

Django Web 框架有自己内置的对象-关系映射模块,通常被称作 “the Django ORM” 或 “Django’s ORM”。

Django 的 ORM 适用于简单或中等复杂度的数据库操作。但是,人们通常抱怨说:该 ORM 基于复杂的查询操作转化而成的 SQL 语句,相比于直接用 SQL 编写或者用 SQLAlchemy 产生的语句,更加的繁琐。

从技术上来说可以直接使用低层的 SQL,但是这种方式会将查询与某个特定的数据库绑定在一起。该 ORM 与 Django 是紧密捆绑的,因而要想用 SQLAlchemy 来代替默认的 ORM 当前还是一门技术活。考虑到一些 Django 核心贡献者都认为默认的 ORM 被 SQLAlchemy 替代只是时间问题。但要想实现该目标还有很多路要走,可能会在 Django 1.9 或之后 的版本才会实现。

因为大多数 Django 项目都使用默认的 ORM,因此我们最好多了解下一些高级的使用案例和工具,从而能更好地使用该框架。

SQLAlchemy

SQLAlchemy 是个广受好评的 Python ORM。它的抽象层做的 ”恰当好处“,并且在绝大多数情况下,相比 Django ORM,它能简化复杂数据库查询语句的编写工作。在 Flask 中,SQLAlchemy 通过 Flask-SQLAlchemy 扩展而被用作数据库 ORM。

Peewee

Peewee 这个 Python ORM 的目标是: 比 SQLAlchemy “更简单、更小及更加 hackable“ 。Peewee 的核心作者使用的比喻为: Peewee 对于 SQLAlchemy,就像 SQLite 对于 PostgreSQL。一个 ORM 无需适用于所有的使用情况也能被认为是有用的。

Pony

Pony ORM 是另一种 Python ORM,它的许可模型有些许不同。该项目使用多种许可。Pony 用于开源项目是免费的,但是用于商业项目需要 商业许可。该许可只需要一次性费用,无需周期付费。

SQLObject

SQLObject 这个 ORM 自 2003 年前 就已处于活跃的开源开发了。

模式迁移

模式迁移,例如添加一个列到数据库的某个表格中等操作,从技术上来说都不属于 ORM 范畴。但是,由于 ORM 通常是间接地操作数据库(在多数情况下需开发人员自担风险),因此在应用程序项目中,执行模式迁移的库通常和 Python ORM 一起使用。

数据库模式迁移是一个复杂的主题,需要专门的一页来讲解。现在,我们粗略地将模式迁移相关资源列在 ORM 链接之后。

通用 ORM 相关资源

  • 还有一个网页就 什么是 ORM 进行了细致的概述。

  • 这个 GitHub 上的示例项目 将这个相同的 Flask 应用用多个不同的 ORM 进行了实现: SQLAlchemy、 Peewee、 MongoEngine、 stdnet 及 PonyORM。

  • Martin Fowler 在一篇文章中提到了 人们对 ORM 的厌恶,讲述了 ORM 是如何被误用的,及说明它们对开发人员其实是有帮助的。

  • 如果你被连接器之间的区别搞糊涂了,比如 MySQL-python 和一个像 SQLAlchemy 等的 ORM,那么看下针对这个主题的 StackOverflow 上的解答

Django ORM 相关资源

SQLAlchemy 相关资源

Peewee 相关资源

Pony ORM 相关资源

SQLObject 相关资源