这篇文章主要盘点 Node.js 生态的 ORM 或者数据操作工具库,这些 ORM 或者工具库能够帮助我们快捷的操作数据库。
一、直接操作 SQL
- 🚀mysql2 MySQL/MariaDB
- 🚀pg PostgreSQL
- 🚀sqlite3 sqlite3
- 🚀better-sqlite3 better-sqlite3
- 🚀mssql Microsoft SQL Server
- 🚀mongodb MongoDB
- 🚀ioredis redis
- 🚀es @elastic/elasticsearch
如果你熟练使用 sql 语句,其实也不需要 ORM, ORM 就是图方便。下面我们就是开始了解 Node.js 社区的各种 ORM或者 ODM。
二、Prisma ORM
Prisma 是最为流行的 ORM, 目前 Prisma 的模型已经开始支持多文件,这解决一个文件中写所有的问题。
2.1)资源
- 🌹prisma prisma 官方网站
- 🌹prisma docs 文档库
- 🌹prisma blog 博客(会发一些新的消息和新的功能)
- 🌹prisma 代码托管地址
- 🌹prisma npm 包管理
2.2)Prisma 特性
- 🌴基于 TypeScript 类型安全,自动生成操作数据 API
- 🌴自创了 Prisma.schema PSL 文件,简洁明了(目前已经支持多文件)
- 🌴多数据库抽象支持,无需关注底层
- 🌴良好的 CLI 命令支持,自动化迁移(不是数据库之间)
- 🌴良好的文档和社区支持
2.3)Primsa 使用示例
使用 Prisma 你需要熟悉 Primsa 使用流程:
prisma -> init -> schema -> migrate -> 使用 primsa 客户客户端操作数据库。
cd your_dir
pnpm init
pnpm add prisma ts-node -D
pnpm npx prisma init # 默认是 pg
写一个 User
模型
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
password String
}
🉑现在 Prisma 已经支持多个 schema 写模型,并且 vscode 插件也得到了很好的跳转支持。所以 Prisma 是值得学习和使用的。
- 🍎使用迁移生成客户端
npx prisma migrate dev --name init
- 使用客户端操作 api
import { PrismaClient } from '@prisma/client'
const prisma = new PrismaClient()
async function user() {
const user = await primsa.user.create({
email: 'your_email',
name: 'your_name',
password: 'your_password'
})
return user
}
user().then((user) => { /* handler*/}).catch(() => /* catch error*/)
- 🍎prisma studio
npx prisma studio # 默认端口 5555
提供一个浏览器版本的数据库操作软件,可以很直观的观察和处理数据。
如果下载有问题,设置环境变量 PRISMA_ENGINES_MIRROR=https://registry.npmmirror.com/-/binary/prisma
。
三、TypeORM
3.1)资源
3.2)TypeORM 特性
- 🌴基于 TypeScript 类型安全,支持 JavaScript/ESM, Class 实体、语法灵活
- 🌴支持 DataMapper 和 ActiveRecord 两种模式
- 🌴直接关系模型(关联,单项、双向、自引用、连接)
- 🌴连接池、支持过数据库实例
- 🌴多数据库支持
- 🌴自动迁移和生成迁移文件
- ...
3.3)TypeORM 使用示例
使用 TypeORM 你需要熟悉 TypeORM 使用流程:
pnpm install typeorm reflect-metadata
pnpm install @types/node -D
# 安装驱动器,驱动器其实就是之前提到直接操作 sql 的库。
配置 typescript 的配置文件
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
- 🍎使用 cli 快速开始
npx typeorm init --name MyProject --database postgres
typeorm -> Entity -> Repository -> save/find/findOneBy/remove
- 定义实体类(并配置列属性)
import { Entity, PrimaryGeneratedColumn, Column } from "typeorm"
@Entity()
export class User {
@PrimaryGeneratedColumn() // 自增长列
id: number
@Column()
firstName: string
@Column()
lastName: string
@Column()
age: number
}
- 🍎初始化 DataSource
import "reflect-metadata"
import { DataSource } from "typeorm"
import { User } from "./entity/User"
export const AppDataSource = new DataSource({
// other...
entities: [User],
})
- 🍎操作实体类型
import { AppDataSource } from "./data-source"
import { User } from "./entity/User"
AppDataSource.initialize().then(async () => {
const user = new User(); // 实例化实体,然后修改实体属性
user.firstName = "Timber"
user.lastName = "Saw"
user.age = 25
await AppDataSource.manager.save(user) // 保存实体
const users = await AppDataSource.manager.find(User) // 查找实体
}).catch(error => console.log(error))
通过以上操作可以看出 typeorm 亲近 TypeScript 的 class, 在 class 基础上操作数据库。
四、Sequelize
4.1)资源
4.2)Sequelize 特性
- 🍎TypeScript 和 Node.js ORM 数据库支持
- 🍎多种数据库支持,支持事务
4.3)Sequelize 使用示例
pnpm add express sequelize sqlite3
npx sequelize-cli init
sqlite3 可能会安装的慢一点,可能是 node-pre-gyp 造成的。可能需要切换源。
cli 会自动创建:
- 🌴
config/config.json
配置文件 - 🌴
migrations
迁移文件夹 - 🌴
models
模型文件夹 - 🌴
seeders
种子文件夹 - 执行迁移命令
npx sequelize-cli db:migrate
, 会自动生成SquelizeMeta
表只有一个字段name
添加模型文件并执行迁移:
'use strict';
const { Model, DataTypes } = require('sequelize');
module.exports = (sequelize) => {
class User extends Model {
static associate(models) {
// Define associations here
}
}
User.init(
{
firstName: DataTypes.STRING,
lastName: DataTypes.STRING,
email: {
type: DataTypes.STRING,
unique: true,
},
age: DataTypes.INTEGER,
},
{
sequelize,
modelName: 'User',
}
);
return User;
};
npx sequelize-cli db:migrate
迁移之后数据中就会添加 User 表。基于 rexpress 写一个接口操作数据
const express = require('express');
const router = express.Router();
const db = require('../models');
router.post('/', async (req, res) => {
try {
const { firstName, lastName, email, age } = req.body;
const newUser = await db.User.create({ firstName, lastName, email, age });
res.json(newUser);
} catch (error) {
res.status(500).json({ error: 'An error occurred while creating the user' });
}
});
module.exports = router;
五、Mongoose (ODM)
5.1)资源
- 🌹mongoosejs mongoosejs 官方网站和文档
- 🌹mongoosejs 代码托管地址
- 🌹mongoosejs npm 包管理
5.2)Mongoose 特性
- 🍎Mongoose 专门为 mongodb 的 ODM 对象文档模型
- 🍎箱即用的内置类型转换、验证、查询构建、业务逻辑挂钩等
pnpm add express mongoose
5.3)Mongoose 使用示例
从 schema 到 model 模型,再到操作模型
const mongoose = require('mongoose');
mongoose.connect('mongodb://127.0.0.1:27017/test');
const animalSchema = new Schema({
name: String,
password: string
});
const AnimalModel = mongoose.model('Animal', animalSchema);
const animals = await Animal.find();
const animal = await Animal.findById(id);
const updatedAnimal = await Animal.findByIdAndUpdate(id, { name, password }, { new: true });
const deletedAnimal = await Animal.findByIdAndDelete(id);
六、drizzle
6.1)drizzle 资源
- 🌹drizzle drizzle 官方网站
- 🌹drizzle docs 文档库
- 🌹drizzle 代码托管地址
- 🌹drizzle npm 包管理
6.2)drizzle 特性
- 🍎Drizzle ORM 是一个有头的无头 TypeScript ORM 🐲
- 🍎如果您了解 SQL,那么您就了解 Drizzle。
- 🍎支持主流的数据库和 serverless
6.3)drizzle 使用示例
pnpm add drizzle-orm dotenv better-sqlite3 drizzle-kit
pnpm add -D esno
drizzle-orm 有众多的 数据库
相关的库的支持。
有一个公式:drizzle + client/connection/... -> migrate -> db, 主要是操作这个 db。
- 🍎创建 schema.ts 文件
import { integer, sqliteTable, text } from "drizzle-orm/sqlite-core";
import { sql } from "drizzle-orm";
export const users = sqliteTable('users', {
id: text('id'),
textModifiers: text('text_modifiers').notNull().default(sql`CURRENT_TIMESTAMP`),
intModifiers: integer('int_modifiers', { mode: 'boolean' }).notNull().default(false),
});
export type User = typeof users.$inferSelect
export type InsertUser = typeof users.$inferInsert
- 🍎配置文件
import { defineConfig } from 'drizzle-kit';
export default defineConfig({
schema: './schema.ts',
out: './drizzle',
dialect: 'sqlite', // 'postgresql' | 'mysql' | 'sqlite'
});
迁移一次:
pnpm drizzle-kit generate
- 🍎创建 db.ts
// src/db.ts
import * as schema from "../schema";
import Database from "better-sqlite3";
import { drizzle } from "drizzle-orm/better-sqlite3";
const sqlite = new Database("sqlite.db");
export const db = drizzle(sqlite, { schema });
- 创建迁移文件
// src/migrate.ts
import "dotenv/config";
import { db } from "./db";
import { migrate } from "drizzle-orm/better-sqlite3/migrator";
// This will run migrations on the database, skipping the ones already applied
async function migrate_run() {
return await migrate(db, { migrationsFolder: "./drizzle" });
}
migrate_run()
.then((res) => console.log(res))
.catch((error) => console.log(error));
执行迁移:
pnpm tsx src/migrate.ts
sqlite 数据库包含两条个表:
__dirzzle_migrations
users
后面就可以愉快的加入到框架中了,总体而言drizzle 有 ORM 模型,会生成 sql 语句,需要迁移,灵活性较强。
七、mikro-orm
7.1)mikro-orm 资源
- 🌹mikro mikro 官方网站
- 🌹mikro docs 文档库
- 🌹mikro blog 博客(会发一些新的消息和新的功能)
- 🌹mikro 代码托管地址
- 🌹mikro npm 包管理
7.2)mikro-orm 特性
- Node.js 的 TypeScript ORM 基于数据映射器、工作单元和身份映射模式。
- 支持主流的数据库
7.3)示例
本示例基于 sqlite,主要分为以下几个步骤:
- 🍎安装依赖
pnpm add express @mikro-orm/core @mikro-orm/better-sqlite @mikro-orm/sql-highlighter
- 🍎定义配置文件
import { defineConfig } from '@mikro-orm/better-sqlite';
import { SqlHighlighter } from '@mikro-orm/sql-highlighter';
export default defineConfig({
dbName: 'your_db',
highlighter: new SqlHighlighter(),
});
在 package.json 中指定配置文件位置:
"mikro-orm": {
"configPaths": [
"./app/mikro-orm.config.js"
]
}
- 🍎定义一个实体
'use strict';
import { Collection, EntitySchema } from '@mikro-orm/core';
import { Book } from './Book.js';
import { BaseEntity } from './BaseEntity.js';
export class Author extends BaseEntity {
constructor(name, email) {
super();
this.name = name;
this.email = email;
this.termsAccepted = false;
}
}
Author.beforeDestroyCalled = 0;
Author.afterDestroyCalled = 0;
export const schema = new EntitySchema({
class: Author,
extends: 'BaseEntity',
properties: {
name: { type: 'string' },
email: { type: 'string' },
age: { type: 'number', nullable: true },
termsAccepted: { type: 'boolean' },
identities: { type: 'string[]', nullable: true },
born: { type: 'Date', nullable: true },
books: {
kind: '1:m',
mappedBy: 'author',
type: 'Book',
},
favouriteBook: {
kind: 'm:1',
type: 'Book',
nullable: true,
},
},
});
- 🍎在 express 中间件中全局注册
import { EntityManager, EntityRepository, MikroORM, RequestContext } from '@mikro-orm/better-sqlite';
import config from './mikro-orm.config.js';
import { Author } from './entities/index.js';
export const DI = {};
DI.orm = await MikroORM.init(config);
DI.em = DI.orm.em;
DI.authors = DI.orm.em.getRepository(Author); // 指定 DI Author 方便操作
app.use((req, res, next) => {
RequestContext.create(DI.orm.em, next);
req.di = DI;
});
- 🍎获取和修改
import { DI } from '../server.js';
router.get('/', async (req, res) => {
const authors = await DI.authors.findAll({
populate: ['books'],
orderBy: { name: QueryOrder.DESC },
limit: 20,
});
res.json(authors);
});
router.post('/', async (req, res) => {
if (!req.body.name || !req.body.email) {
res.status(400);
return res.json({ message: 'One of `name, email` is missing' });
}
try {
const author = DI.em.create(Author, req.body);
await DI.em.flush();
res.json(author);
} catch (e) {
return res.status(400).json({ message: e.message, stack: e.stack });
}
});
在开始运行之前还有一个重要的步骤就是: npx mikro-orm schema:create -r
生成 schema
。然后就可以自由的操作数据了,添加功能。当然 cli 在后续也十分重要,根据自己的情况学习和补充。
八、Knex.js
8.1)资源
8.2)Knex.js 特性
- 🌳定位是 基于 JavaScript 的 SQL 查询生成器。
- 🌳主流的数据库均支持、支持 TypeScript。
Knex.js 干了一件非常核心的事情,就是将原来的 SQL 语句抽象为 JavaScript 函数。我们来看看对比:
// 初始化 Knex
const knex = require('knex')({
client: 'mysql', // 或者 'pg', 'sqlite3', 'oracle' 等
connection: {
host: '127.0.0.1',
user: 'your_database_user',
password: 'your_database_password',
database: 'myapp_test'
}
});
// 构建一个 SELECT 查询: SELECT * FROM users WHERE id = 1;
knex.select('*').from('users').where('id', 1)
.then(rows => {
console.log(rows);
})
.catch(err => {
console.error(err);
});
// 插入数据: INSERT INTO users (name, age) VALUES ('John Doe', 28);
knex('users').insert({name: 'John Doe', age: 28})
.then(() => {
console.log('Data inserted');
})
.catch(err => {
console.error(err);
});
// 更新数据: UPDATE users SET name = 'Jane Doe' WHERE id = 1;
knex('users').where('id', 1).update({name: 'Jane Doe'})
.then(() => {
console.log('Data updated');
})
.catch(err => {
console.error(err);
});
// 删除数据: DELETE FROM users WHERE id = 1;
knex('users').where('id', 1).del()
.then(() => {
console.log('Data deleted');
})
.catch(err => {
console.error(err);
});
8.3)构建在 Knex.js 之上
九、小结
这些 ORM 或者库,使用起来都差不多,功能也差不多,多数据库支持,有的基于使用模型,有的有基于实体类,然后在其基础上操作数据库。像 prisma 和 drizze 还提供了 studio 这种可是工具。有的提供了数据库的迁移工具,体验更加完整,整体你需要熟悉模型、需要熟悉实体、需要 sql 和 ORM 的映射,还需要 cli 工具,这一整套内容。最后希望能够帮助到大家。