一、概述
面向对象编程和关系型数据库,都是目前最流行的技术,但是它们的模型是不一样的。
面向对象编程把所有实体看成对象(object),关系型数据库则是采用实体之间的关系(relation)连接数据。很早就有人提出,关系也可以用对象表达,这样的话,就能使用面向对象编程,来操作关系型数据库。
简单说,ORM 就是通过实例对象的语法,完成关系型数据库的操作的技术,是"对象-关系映射"(Object/Relational Mapping) 的缩写。
ORM 把数据库映射成对象。
- 数据库的表(table) --> 类(class)
- 记录(record,行数据)–> 对象(object)
- 字段(field)–> 对象的属性(attribute)
加粗样式
举例来说,下面是一行 SQL 语句。
SELECT id, first_name, last_name, phone, birth_date, sex
FROM persons
WHERE id = 10
程序直接运行 SQL,操作数据库的写法如下。
res = db.execSql(sql);
name = res[0]["FIRST_NAME"];
改成 ORM 的写法如下。
p = Person.get(10);
name = p.first_name;
一比较就可以发现,ORM 使用对象,封装了数据库操作,因此可以不碰 SQL 语言。开发者只使用面向对象编程,与数据对象直接交互,不用关心底层数据库。
总结起来,ORM 有下面这些优点。
-
数据模型都在一个地方定义,更容易更新和维护,也利于重用代码。
-
ORM 有现成的工具,很多功能都可以自动完成,比如数据消毒、预处理、事务等等。
-
它迫使你使用 MVC 架构,ORM 就是天然的 Model,最终使代码更清晰。
-
基于 ORM 的业务代码比较简单,代码量少,语义性好,容易理解。
-
你不必编写性能不佳的 SQL。
但是,ORM 也有很突出的缺点。 -
ORM 库不是轻量级工具,需要花很多精力学习和设置。
-
对于复杂的查询,ORM 要么是无法表达,要么是性能不如原生的 SQL。
-
ORM 抽象掉了数据库层,开发者无法了解底层的数据库操作,也无法定制一些特殊的 SQL。
二、命名规定
许多语言都有自己的 ORM 库,最典型、最规范的实现公认是 Ruby 语言的 Active Record。Active Record 对于对象和数据库表的映射,有一些命名限制。
(1)一个类对应一张表。类名是单数,且首字母大写;表名是复数,且全部是小写。比如,表books对应类Book。
(2)如果名字是不规则复数,则类名依照英语习惯命名,比如,表mice对应类Mouse,表people对应类Person。
(3)如果名字包含多个单词,那么类名使用首字母全部大写的骆驼拼写法,而表名使用下划线分隔的小写单词。比如,表book_clubs对应类BookClub,表line_items对应类LineItem。
(4)每个表都必须有一个主键字段,通常是叫做id的整数字段。外键字段名约定为单数的表名 + 下划线 + id,比如item_id表示该字段对应items表的id字段。
三、示例库
下面使用 OpenRecord 这个库,演示如何使用 ORM。
OpenRecord 是仿 Active Record 的,将其移植到了 JavaScript,而且实现得很轻量级,学习成本较低。我写了一个示例库,请将它克隆到本地。
$ git clone https://github.com/ruanyf/openrecord-demos.git
然后,安装依赖。
$ cd openrecord-demos
$ npm install
示例库里面的数据库,是从网上拷贝的 Sqlite 数据库。它的 Schema 图如下(PDF 大图下载)。
四、连接数据库
使用 ORM 的第一步,就是你必须告诉它,怎么连接数据库(完整代码看这里)。
// demo01.js
const Store = require('openrecord/store/sqlite3');
const store = new Store({
type: 'sqlite3',
file: './db/sample.db',
autoLoad: true,
});
await store.connect();
连接成功以后,就可以操作数据库了。
五、Model
5.1 创建 Model
连接数据库以后,下一步就要把数据库的表,转成一个类,叫做数据模型(Model)。下面就是一个最简单的 Model(完整代码看这里)。
// demo02.js
class Customer extends Store.BaseModel {
}
store.Model(Customer);
上面代码新建了一个Customer类,ORM(OpenRecord)会自动将它映射到customers表。使用这个类就很简单。
// demo02.js
const customer = await Customer.find(1);
console.log(customer.FirstName, customer.LastName);
上面代码中,查询数据使用的是 ORM 提供的find()方法,而不是直接操作 SQL。Customer.find(1)表示返回id为1的记录,该记录会自动转成对象,customer.FirstName属性就对应FirstName字段。
5.2 Model 的描述
Model 里面可以详细描述数据库表的定义,并且定义自己的方法(完整代码看这里)。
// demo03.js
class Customer extends Store.BaseModel {
static definition(){
this.attribute('CustomerId', 'integer', { primary: true });
this.attribute('FirstName', 'string');
this.attribute('LastName', 'string');
this.validatesPresenceOf('FirstName', 'LastName');
}
getFullName(){
return this.FirstName + ' ' + this.LastName;
}
}
上面代码告诉 Model,CustomerId是主键,FirstName和LastName是字符串,并且不得为null,还定义了一个getFullName()方法。
实例对象可以直接调用getFullName()方法。
// demo03.js
const customer = await Customer.find(1);
console.log(customer.getFullName());
更多信息:http://www.ruanyifeng.com/blog/2019/02/orm-tutorial.html