Prisma 是一个 开源 的下一代 ORM。它包含了以下部分:
Prisma 客户端可以被用在 任何 Node.js 或 TypeScript 后端应用中(包括 Serverless 应用和微服务)。可以是一个 REST API,一个 GraphQL API,一个 gRPC API,或任何其他需要数据库的东西。
本文将快速介绍 Prisma 基本使用方法。
直接访问 官方示例
创建 typescript 工程,使用 gts:
mkdir -p hello-prisma
cd hello-prisma
npx gts init -y
npm install -D @vercel/ncc
安装 prisma 依赖:
npm install -D prisma
稍微调整下tsconfig.json
的内容如下(gts 使用的module
配置为commonjs
,主要这个需要调整下):
{
"extends": "./node_modules/gts/tsconfig-google.json",
"compilerOptions": {
"rootDir": "src",
"outDir": "dist",
"module": "ES2020",
"target": "ES2020",
"lib": ["ES2020"],
"esModuleInterop": true,
"moduleResolution": "node"
},
"include": ["src/**/*.ts", "test/**/*.ts"]
}
初始化 prisma 模板:
$ npx prisma init
✔ Your Prisma schema was created at prisma/schema.prisma
You can now open it in your favorite editor.
Next steps:
1. Set the DATABASE_URL in the .env file to point to your existing database. If your database has no tables yet, read https://pris.ly/d/getting-started
2. Set the provider of the datasource block in schema.prisma to match your database: postgresql, mysql, sqlite, sqlserver or mongodb (Preview).
3. Run prisma db pull to turn your database schema into a Prisma schema.
4. Run prisma generate to generate the Prisma Client. You can then start querying your database.
More information in our documentation:
https://pris.ly/d/getting-started
prisma cli 已经帮我们生成了一个 schema 模板 prisma/schema.prisma
:
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
以及一个dotenv
配置文件.env
:
# Environment variables declared in this file are automatically made available to Prisma.
# See the documentation for more detail: https://pris.ly/d/prisma-schema#using-environment-variables
# Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server and MongoDB (Preview).
# See the documentation for all the connection string options: https://pris.ly/d/connection-strings
DATABASE_URL="postgresql://johndoe:randompassword@localhost:5432/mydb?schema=public"
调整数据库连接方式,在这个基础上追加表结构定义就可以了。比如我们定义一个 user
表:
model User {
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
email String @unique
name String?
}
model 命名需要满足大写驼峰式,并且不能使用保留字,参考官方文档 prisma-schema-reference#naming-conventions
运行 npx prisma migrate dev --name init
即可创建数据库并生成 migration 脚本:
-- CreateTable
CREATE TABLE "User" (
"id" SERIAL NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"email" TEXT NOT NULL,
"name" TEXT,
CONSTRAINT "User_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");
本地进行快速原型设计还可以用
db push
直接修改数据库,而不创建 migrate 脚本,这在本地快速迭代,不关心中间的变更场景下很有用。但在产品环境期望不丢失数据而安全迁移数据库的场景下,应该用
migrate deploy
而非db push
。有关db push
的描述以及与migrate
的对比参见官方文档: db-push
修改 schema ,增加一个字段:
model User {
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
email String @unique
name String?
// 增加一个 foo 字段,定义为 VARCHAR 类型
foo String? @db.VarChar(10)
}
运行 npx prisma migrate dev --name add_foo_column
,生成的 sql 如下:
-- AlterTable
ALTER TABLE "User" ADD COLUMN "foo" VARCHAR(10);
以后修改 prisma/schema.prisma
之后再次运行 npx prisma migrate dev
即可将改动同步到数据库中。如果追加 --create-only
参数,则只生成 migration.sql 文件,不会同步到数据库中。
开发环境生成 migrate 脚本(主要是 migrate dev
和 migrate reset
命令需要用 shadow database)需要有数据库的 createdb 权限,否则会报错。如果你的环境无法提供 createdb 权限,你需要单独创建一个 shadow database,在 datasource db {}
配置块中额外添加 shadowDatabaseUrl = env('SHADOW_DATABASE_URL')
指定,参考官方文档: shadow-database
产品环境直接用 migrate deploy
或 migrate resolve
运行 migrate 脚本,无需创建 shadow database。
注意 Prisma 维护了一组默认的 schema 到数据库类型的映射,如果不满足你的需求,可以追加 @db.<database_type>
参数变更,如 String
类型默认映射为数据库的 TEXT
类型,可能你希望映射成 VARCHAR
类型:
model User {
userName String @db.VarChar(10)
}
prisma schema 所有支持的字段类型以及可以映射的数据库类型(@db.<database_type>
)可以参考官方文档: model-field-scalar-types
其他常用的 schema 定义
prisma 支持两种注释 //
和 ///
,前者只是代码普通注释,后者会出现在节点语法树中(AST),以 Data Model Meta Format (DMMF)形式呈现:
/// This comment will get attached to the `User` node in the AST
model User {
/// This comment will get attached to the `id` node in the AST
id Int @default(autoincrement())
// This comment is just for you
weight Float /// This comment gets attached to the `weight` node
}
// This comment is just for you. It will not
// show up in the AST.
/// This comment will get attached to the
/// Customer node.
model Customer {}
当前 prisma 还不支持数据库级别的表和字段注释(DDL COMMENT),参见官方issue
目前只能通过
--create-only
参数生成 sql 之后手改
为了规避数据库的大小写问题,一般数据库最佳实践都是表和字段统一使用小写下划线形式命名。对于 prisma 需要使用 @@map
和 @map
函数定义:
model User {
id Int @id
// @map 用于定义列名
cardId Int @map("card_id")
// @@map 用于定义表名
@@map("user")
}
对应生成的 sql 如下:
-- CreateTable
CREATE TABLE "user" (
"id" INTEGER NOT NULL,
"card_id" INTEGER NOT NULL,
CONSTRAINT "user_pkey" PRIMARY KEY ("id")
);
如果要对索引、约束等命名的话参考官方文档: names-in-underlying-databas#using-custom-constraint–index-names
@default
)@default
可以定义字段的默认值,参数可以是静态的固定值,如5
, false
等等,也可以是 prisma 提供的几种函数,如autoincrement()
, uuid()
, now()
等等,参见文档: prisma-schema-reference#default
model User {
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
}
可以定义字段为枚举类型:
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
role Role @default(USER)
}
enum Role {
USER
ADMIN
}
对应 sql:
-- CreateEnum
CREATE TYPE "Role" AS ENUM ('USER', 'ADMIN');
-- CreateTable
CREATE TABLE "User" (
"id" SERIAL NOT NULL,
"email" TEXT NOT NULL,
"name" TEXT,
"role" "Role" NOT NULL DEFAULT E'USER',
CONSTRAINT "User_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");
@updatedAt
)这个属性可以自动设置更新时间:
model Post {
id String @id
createdAt DateTime @default(now()) @db.Timestamptz
updatedAt DateTime @updatedAt @db.Timestamptz
}
@@index
)直接使用 @@index
函数可以创建索引:
model Post {
id Int @id @default(autoincrement())
title String
content String?
@@index([title, content])
}
如果要自定义索引类型,可以用 npx prisma migrate dev --create-only
生成 migration.sql
文件,然后在 migration.sql
中修改。
如果期望在 schema 文件中定义,则需要启用 extendedIndexes
preview feature:
generator client {
provider = "prisma-client-js"
previewFeatures = ["extendedIndexes"]
}
就可以在 @@index
中使用 type
参数指定索引类型了:
model Example {
id Int @id
value Int
@@index([value], type: Hash)
}
有关 @@index
部分的文档参考: prisma-schema-reference#index
extendedIndexes
部分的文档参考: indexes
@id
/ @@id
)单主键
model Table {
id Int @id @default(autoincrement())
}
生成 sql:
-- CreateTable
CREATE TABLE "Table" (
"id" SERIAL NOT NULL,
CONSTRAINT "Table_pkey" PRIMARY KEY ("id")
);
联合主键
model Table {
firstName String @db.VarChar(10)
lastName String @db.VarChar(10)
@@id([firstName, lastName])
}
生成 sql:
-- CreateTable
CREATE TABLE "Table" (
"firstName" VARCHAR(10) NOT NULL,
"lastName" VARCHAR(10) NOT NULL,
CONSTRAINT "Table_pkey" PRIMARY KEY ("firstName","lastName")
);
@unique
/ @@unique
)单列唯一性
model User {
id Int @id @default(autoincrement())
email String? @unique
name String
}
多列唯一性
model User {
id Int @id
firstname Int
lastname Int
card Int?
@@unique([firstname, lastname, card])
}
默认字段类型都是非空的,可空给字段类型加上 ?
就行了:
model User {
id Int @id
firstname Int
lastname Int
card Int?
}
@relation
)稍微麻烦些,需要同时在两个 model 定义互相的对应关系,如下所示:
model User {
id Int @id
firstname String @db.VarChar(10)
lastname String @db.VarChar(10)
Post Post[]
}
model Post {
id Int @id
author User @relation(fields: [userId], references: [id])
content String @db.VarChar(200)
userId Int
}
生成 sql:
-- CreateTable
CREATE TABLE "User" (
"id" INTEGER NOT NULL,
"firstname" VARCHAR(10) NOT NULL,
"lastname" VARCHAR(10) NOT NULL,
CONSTRAINT "User_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Post" (
"id" INTEGER NOT NULL,
"content" VARCHAR(200) NOT NULL,
"userId" INTEGER NOT NULL,
CONSTRAINT "Post_pkey" PRIMARY KEY ("id")
);
-- AddForeignKey
ALTER TABLE "Post" ADD CONSTRAINT "Post_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
这里有个比较方便的方法可以快速创建出这种映射关系,就是直接利用 prisma format
的功能即可。
比如先像 Hibernate 那样创建表关联:
model User {
id Int @id
firstname String @db.VarChar(10)
lastname String @db.VarChar(10)
}
model Post {
id Int @id
author User
content String @db.VarChar(200)
}
然后在命令行下执行 npx prisma format
,或者在 vscode 安装插件Prisma后直接在 vscode 格式化即可自动生成上述 model 映射关系。
更多映射关系(一对一,一对多,多对多)的定义可以参考官方文档: relations
定义好 schema,完成 migrate 之后,需要通过 prisma-client
调用 schema 完成数据库的操作。
npm install @prisma/client
第一次安装 @prisma/client
之后,会自动调用 prisma generate
生成定制化的 client,以后每次修改了 schema 之后,需要手工调用 npx prisma generate
生成新的 client model。
在项目中引入 PrismaClient
组件即可:
import { PrismaClient } from "@prisma/client";
const prisma = new PrismaClient();
想要打印 sql 可以这么配置:
const prisma = new PrismaClient({
log: ["query", "info", "warn", "error"],
});
PrismaClient 默认使用连接池,关于连接池的配置可以参考官方文档: connection-pool
有关详细的基本 CURD 操作直接参考官方文档完整的示例: crud
都比较简单,一目了然。基本的基于 model 的 CURD 函数主要有下面几个:
此外,还可以通过 select
属性控制返回的字段:
// Returns an object or null
const getUser: object | null = await prisma.user.findUnique({
where: {
id: 22,
},
select: {
email: true,
name: true,
},
});
更多详情参见: select-fields
当前 prisma 还不支持 exclude 排除特定的字段(如 password),官方正在对这个设计进行探讨中,参见 issue: prisma/prisma#5042
如果用到类似于 join
之类的关联查询,可以参考文档: relation-queries
对于 JSONB
相关的查询,prisma API 也有支持: working-with-json-fields
每个基本的 CURD 操作都会在单独的事务中运行(打印 sql 的时候可以看到包装有BEGIN...COMMIT
),此外,还可以明确使用 $transaction
函数包装多个操作在一个事务中:
const [posts, totalPosts] = await prisma.$transaction([
prisma.post.findMany({ where: { title: { contains: "prisma" } } }),
prisma.post.count(),
]);
prisma 提供了一些包含 Raw
关键字的函数,可以直接使用 sql,如:
const result = await prisma.$queryRaw`SELECT * FROM User`;
const result: number =
await prisma.$executeRaw`UPDATE User SET active = true WHERE emailValidated = true`;
所有直接调用 sql 的函数可以参阅官方文档: raw-database-access
觉得有帮助的话,不妨考虑购买付费文章来支持我们 🙂 :
付费文章