MidwayJS 全栈开发(四)ORM 技术选型
MidwayJS 全栈开发(四)ORM 技术选型
前言
上一篇内容回顾:MidwayJS 全栈开发(三)SQL 技术选型 。
本篇文章将围绕操作数据库的工具 ORM 进行讲解,简单串一下 ORM 的概念以及技术选型,方便我们有个整体的认识。
ORM 介绍
ORM 全称是:Object Relational Mapping(对象关系映射),主要目的是为了帮助我们更快且更简单的访问和操作数据库表。
ORM 工作机制
那么它是如何工作的呢?
顾名思义,就是以面向对象的形式与数据库中的字段定义以及数据建立映射,对于数据库的 CRUD 也会提供对应语法映射的可调用函数方法。
这里我们用代码说话,以一个常见场景来举例,新增一个用户信息到表中(假设表名为 users
):
- 使用标准 SQL 语句来实现:
const sql = `INSERT INTO users (id, username, nickname, password)
VALUES (null, 'test', '不败花丶', 'myMassword');
`;
// SQLConnection 表示与数据库的连接实例(此处省略声明)
// `.query()` 表示执行 sql 语句
SQLConnection.query(sql, (error, results, fields) => {
console.log(results);
});
- 使用 ORM 来实现:
// `User` 是我们使用 ORM 语法定义的与`users`表关联的类实例(此处省略声明)
// 其中默认内置了很多基础的 SQL 表操作方法
// `.create(object)` 就表示在表中新增一条记录的方法
const results = await User.create({
username: 'test',
nickname: '不败花丶',
password: 'myMassword',
});
console.log(results);
这里我们可以看到,ORM 其实就是使用对象封装了对数据库的访问: - 使用对象映射:表示数据库表中的数据 - 使用函数调用:代替数据库表的 CRUD 操作
对于开发者而言,你可能只需要与数据对象进行交互,而无需关心底层数据库的操作。
流程图.jpg
这里,我们再综合对比下 ORM 相对于传统 SQL 写法的优缺点:
ORM 的优点
- 提高开发效率:只需声明表定义和操作对象,使使用数据库更更简单高效
- 面向对象编程:语法语句更贴合自身编程语言,更加清晰易懂,让代码更易维护
- 降低学习成本:一套通吃,底层抽象封装,抹平语法差异,支持不同主流数据库
- 强大的扩展能力:除了表定义、映射 CRUD,市面上绝大多数 ORM 工具还支持预处理、事务、字段名修改等自动化功能
ORM 的缺点
- 小性能代价:ORM 映射实现是以性能为代价的,提高了开发效率,降低了系统性能
- 不适用复杂SQL查询:对于太复杂的查询,ORM 可能无法表达,要么可能性能极差
小结
ORM 其实就是对象到 SQL 的映射(当然有些 ORM 工具也提供有 SQL 到对象的反向映射)。使用它,可以让数据库操作更加简单。
虽然 ORM 可以提高了我们的开发效率,但是有一定的性能开销。所以一定要合理权衡,普通业务场景可能比较适合,但是对性能要求极高,或者复杂查询场景,可能就得头疼了。
值得一提,ORM 只是一个辅助工具,对于原生 SQL 的学习和使用,作为后端来讲是不可或缺的。比如:使用 ORM 对某个表数据查询耗时较长,你怎么排查、怎么优化呢。
ORM 选型对比
目前 NodeJS 开源社区比较流行的 ORM 大概有这么几个:Sequelize-typescript、TypeORM、Prisma,这里我们逐一进行介绍。
image.png
Sequlize
基本介绍
Sequlize 是目前比较老牌且使用最多的 ORM 工具。目前大多使用 Sequlize-TypeScript 版本,支持类装饰器定义表数据模型,外加类型智能提示,使用更简单。
核心特性
- 基础能力:模型定义、查询、事务、钩子等
- 强大的 CLI:帮助快速初始化、sql迁移等
- 支持 RAW 查询:可以自己编写执行原始的 SQL 语句
- 连接无感:在初始化 ORM 配置用户名密码即可,运行时执行自动连接
- 支持 Typescript:但是支持时间上比较落后,被 TypeORM 抢占先机了
- 支持主流数据库:PostgreSQL、MySQL、SQLite 等
- 社区强大且成熟:非常古老受欢迎,社区资源也非常多
简单示例
这里我们使用 Sequlize-TypeScript 实现一个简单的场景示例:定义一个极简的用户表,并初始化连接数据库,并往表中插入数据。
- 创建与数据库的连接、初始化模型
// sequelize.ts *******************************
import { Sequelize } from "sequelize-typescript";
import { User } from './user.module'
// 创建连接实例
export const sequelize = new Sequelize(
dialect: 'postgres',
username: "username",
password: "password",
database: "database_name",
);
// 初始化数据模型
sequelize.addModels([User]);
- 定义用户表模型
// user.module.ts *******************************
import {
Table,
Column,
Model,
AutoIncrement,
PrimaryKey,
AllowNull,
DataType,
Unique,
} from "sequelize-typescript";
/**
* 用户表模型
*/
@Table({ tableName: 'users' })
export class User extends Model<User> {
@AutoIncrement
@PrimaryKey
@Column
id: number;
@AllowNull(false)
@Unique
@Column(DataType.STRING(32))
username: string;
@AllowNull(false)
@Column(DataType.STRING(128))
password: string;
}
- 往表中插入数据
// demo.ts *******************************
import { sequelize } from './sequelize'
import { User } from './user.module'
// 进行认证连接数据库(一般在应用入口时调用)
await sequelize.authenticate();
// 请求时,往表中插入数据(在接口请求时被调用)
const results = await User.create({ name: '不败花丶' })
console.log(results)
Typeorm
基本介绍
一个完全基于 TypeScript 的 ORM 框架,对 TS 的高度支持。它的目标是始终支持最新的 JavaScript 特性并提供额外的特性以帮助你开发任何使用数据库的应用。
核心特性
- Sequelize 该有的基本都有
- 双模式支持:支持
ActiveRecord
(数据模型和CRUD方法在单独类中,需要继承BaseEntity
基类,写法类同 sequelize 继承Model
一样)和DataMapper
模式(数据模型和CRUD方法分开解耦) - 支持更多数据库:底层支持非关系数据库 MongoDB,这是 Sequlize 目前没有的
简单示例
这里我们使用 Data Mapper
的方式(Data Mapper
可以帮助你保持软件的可维护性,这在更大的应用程序中将更有效)来写一下示例。
- 创建与数据库的连接、初始化模型
// typeorm.ts *******************************
import "reflect-metadata"
import { DataSource } from "typeorm"
import { User } from './user.module'
// 创建连接实例
export const dataSource = new DataSource({
type: "postgres",
username: "username",
password: "password",
database: "database_name",
entities: [User],
})
- 定义用户表模型
// user.module.ts *******************************
import { Entity, PrimaryGeneratedColumn, Column, Unique } from "typeorm";
import { dataSource } from './typeorm'
/**
* 用户表模型
*/
@Entity('users')
@Unique(['username'])
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
username: string;
}
// Data Mapper 模式下,获取实体类的 CRUD 的辅助方法
export const userRepository = dataSource.getRepository(User)
- 往表中插入数据
// demo.ts *******************************
import { dataSource } from './typeorm'
import { User } from './user.module'
// 进行认证连接数据库(一般在应用入口时调用)
await dataSource.initialize()
// 请求时,往表中插入数据(在接口请求时被调用)
const user = new User();
user.username = "不败花丶";
await userRepository.save(user);
Prisma
基本介绍
Prisma 自称是下一代 Node.js 和 TypeScript 的 ORM。通过 PrismaClient 使用的通用数据库抽象取代了传统的 ORM 和自定义数据访问层。用于构建 GraphQL 服务器、 REST API 等。
核心特性
- 基本都支持
- 迁移可控:执行 cli 命令进行数据库表变更或迁移,并附带生成sql语句文件及变更日志等信息,方便在生产率和控制力之间自主把握平衡
- 类型安全:查询到的数据类型都是严格且安全的(不会出现像 TypeORM 联合查询结果字段类型非可选但是使用时却
undefined
的情况) - 结果干净:数据库查询是返回普通 JavaScript 的对象,不带额外数据方法等
- Prisma client:支持多客户端连接,能够更好的适应不同技术,包括 NextJS、GraphQL、NestJS、传统 ResAPI、Apollo 等
- Prisma Studio:现代化的用户界面查看和编辑数据
- 拥有全职维护团队:对 Issue 支持修复相对更及时
简单示例
和上述两者不同,Prisma 使用自己的一套文件语法(schema.prisma
),用来描述数据库相关的配置和表数据模型。 最后,执行 cli 命令就能生成 sql 文件(可以直接在数据库中执行的语句,具备可迁移性),并且自动帮我们创建好表 TABLE
。
- 定义用户表模型 & 数据库连接配置(二合一)
首先,在项目内执行 prisma init
创建如下标准模板,并完成相应数据库连接配置和表模型定义。
// schema.prisma
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = "postgresql://username:password@localhost:3306/database_name"
}
model User {
id Int @id @default(autoincrement())
username String @unique
}
- 执行 CLI 命令
在 scheme.prisma
定义好之后,可以执行以下命令,完成表的创建以及实体(User) TS 类型文件的生成,方便开发者享受 TS 体验。
prisma migrate dev
- 往表中插入数据
import { PrismaClient } from '@prisma/client'
export const prisma = new PrismaClient()
// 请求时,往表中插入数据(在接口请求时被调用)
const user = await prisma.user.create({
data: {
username: '不败花丶', // 有代码提示哦
}
})
小结
本小节简单介绍了不同 ORM 工具的概念和使用。除了 prisma 数据模型定义有点不一样,使用自己的一套语法之外,基本使用上都大同小异,尤其 Sequlize 和 TypeORM 之间。
- 对于业务生产使用,我们建议使用 Sequlize 和 TypeORM,尤其 TypeORM 的
Data Mapper
模式值得推荐。 - 对于 Prisma,非常有可能成为未来的趋势,我打算在本系列课程中尝试使用,感兴趣可以看后面的章节。
全文总结
本篇主要介绍了数据库访问操作的常用工具 ORM,以及社区几种比较流行的 ORM 工具,方便大家选择。
那么,对于 SQL 还不熟悉的初学者而言,它是非常适合快速上手实战的,但不要形成依赖!什么时候使用它也需要合理权衡: - 业务复杂度:简单 vs 复杂 - 需求场景:效率 vs 性能
值得一提的是,但是要明确 ORM 只是一个工具,作为全栈,学习标准的 SQL 语法并学习一门实用的关系数据库管理系统,还是非常有必要的。