关系型数据库
关系型数据库(Relational Database,RDB)是指采用了关系模型来组织数据的数据库。
关系模型包括数据结构(如二维表)、操作指令集合(SQL语句)、完整性约束(表内数据约束、表与表之间的约束)。
SQL(Structured Query Language)是访问和操作关系型数据库的标准语言。
有代表性的关系数据库管理系统(Relational Database Management System,RDBMS)有:
- Oracle Database:甲骨文,支持平台多
- SQL Server:微软,适用于Windows系统
- PostgreSQL:开源,可靠性高
- MySQL:开源,流行易上手,是LAMP(用于Web开发的软件包,Linux、Apache、MySQL、PHP)中的一员
- SQLite:开源,轻量级的嵌入式数据库,无需另起数据库服务,可以嵌入到使用它的应用程序或服务器中
得益于SQL标准化,SQL适用于所有的RDBMS。
在 Node.js 中,使用 RDBMS 除了有针对特定RDBMS开发的 npm 库,也有兼容多RDBMS的 npm 库。考虑迁移数据库的可能性,推荐在生产环境使用兼容多RDBMS的 npm 库。
本文以 SQLite 为例,在 Node.js 中使用以下 npm 库分别实现操作 RDB。
SQLite
js
import { Database } from "sqlite3"
// 创建数据库,或打开已有的数据库
const db = new Database(__dirname + '/demo.db')
// 运行SQL
db.run('CREATE TABLE books (id INTEGER PRIMARY KEY AUTOINCREMENT, title TINYTEXT, introduction TEXT)')
// 关闭数据库
db.close()
Knex
Knex 的定位是SQL查询构建器。它提供一组对Node.js开发者友好API,替代拼写SQL查询语句的过程,并代理开发者与SQL客户端(针对特定 MS 开发的 npm 库)的交互。
js
import knex from 'knex'
// 打开数据库
const client = knex({
client: 'sqlite3',
connection: {
filename: 'sqlite3/demo.db'
},
useNullAsDefault: true
})
// 增: insert into books values (null, "nodejs-roadmap", "A notebook for studying Node.js")
await client('books').insert({
title: 'nodejs-roadmap',
introduction: 'A notebook for studying Node.js',
})
console.log('insert')
// 查: select * from books where title="nodejs-roadmap" limit 1
const selected = await client('books').select().where({title: 'nodejs-roadmap'}).first()
console.log('select', selected)
// 改: update books set title="node.js roadmap" where id in (select id where title="nodejs-roadmap" limit 1)
await client('books').where({title: 'nodejs-roadmap'}).first().update({title: 'node.js roadmap'})
const updated = await client('books').select().where({title: 'node.js roadmap'}).first()
console.log('update', updated)
// 删: delete from books
await client('books').del()
console.log('delete')
// 关闭数据库
client.destroy()
Sequelize
Sequelize 是一个Node.js ORM(Object Relational Mapping,对象关系映射)工具。
ORM工具作用是将数据库表的概念与面向对象编程中的对象概念,通过映射联系起来,以便使用面向对象编程的方式操作关系型数据库。
连接数据库
js
import { Sequelize, DataType } from 'sequelize'
const sequelize = new Sequelize({
dialect: 'sqlite',
storage: 'sqlite3/demo.db'
})
定义对象模型
使用对象描述表结构
js
const Books = sequelize.define(
"books",
{
id: {
type: DataTypes.INTEGER,
allowNull: false,
autoIncrement: true,
primaryKey: true,
},
title: DataTypes.STRING,
introduction: DataTypes.STRING,
},
{
timestamps: false,
}
)
console.log(Books === sequelize.models.books) // true
操作
js
import { Sequelize, DataTypes } from 'sequelize'
const sequelize = new Sequelize({
dialect: 'sqlite',
storage: 'sqlite3/demo.db',
})
const Books = sequelize.define(
'books',
{
id: {
type: DataTypes.INTEGER,
allowNull: false,
autoIncrement: true,
primaryKey: true,
},
title: DataTypes.STRING,
introduction: DataTypes.STRING,
},
{
timestamps: false,
}
)
console.log(Books === sequelize.models.books) // true
// 增: insert into books values (null, "nodejs-roadmap", "A notebook for studying Node.js")
await Books.create({
title: 'nodejs-roadmap',
introduction: 'A notebook for studying Node.js',
})
console.log('insert')
// 查: select * from books where title="nodejs-roadmap" limit 1
const selected = await Books.findAll({
where: {
title: 'nodejs-roadmap',
},
limit: 1,
})
console.log('select', selected[0].dataValues)
// 改: update books set title="node.js roadmap" where id in (select id where title="nodejs-roadmap" limit 1)
await Books.update(
{
title: 'node.js roadmap',
},
{
where: {
title: 'nodejs-roadmap',
},
limit: 1,
}
)
const updated = await Books.findAll()
console.log('update', updated.map((item) => item.dataValues))
// 删:delete from books
await Books.destroy({
truncate: true, // truncate 不能回滚,实际使用的 delete
})
console.log('delete')
Prisma
Prisma 在ORM工具的基础上,使用自定义的 schema 来创建和维护数据库表结构。这样做有效节省了ORM工具重复描述表结构的工作。
schema
以下是 schema.prisma
文件
prisma
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "sqlite"
url = env("DATABASE_URL")
}
model books {
id Int @id @default(autoincrement())
title String
introduction String?
}
运行 prisma CLI 命令,根据 schema 生成 SQLite 数据库文件 demo.db
shell
npx prisma migrate dev --name demo
操作
js
import { PrismaClient } from '@prisma/client'
const prisma = new PrismaClient()
// 增: insert into books values (null, "nodejs-roadmap", "A notebook for studying Node.js")
await prisma.books.create({
data: {
title: 'nodejs-roadmap',
introduction: 'A notebook for studying Node.js',
},
})
console.log('insert')
// 查: select * from books where title="nodejs-roadmap" limit 1
const selected = await prisma.books.findFirst({
where: {
title: 'nodejs-roadmap',
},
})
console.log('select', selected)
// 改: update books set title="node.js roadmap" where id in (select id where title="nodejs-roadmap" limit 1)
await prisma.books.updateMany({
data: {
title: 'node.js roadmap',
},
where: {
id: {
in: (
// 由于分成2次查询,容易遇到并发问题
await prisma.books.findMany({
select: {
id: true,
},
where: {
title: 'nodejs-roadmap',
},
take: 1,
})
).map((item) => item.id),
},
},
})
const updated = await prisma.books.findMany()
console.log('update', updated)
// 删:delete from books
await prisma.books.deleteMany()
console.log('delete')