Prisma 简介
Prisma 是一个现代的 Node.js 和 TypeScript ORM(对象关系映射)工具,它提供了一种类型安全、直观的方式来访问数据库。Prisma 的设计目标是使数据库访问变得更加简单、可靠和高效,同时提供了丰富的功能来满足各种应用场景的需求。
核心特点
- 类型安全:自动生成 TypeScript 类型,提供编译时类型检查,减少运行时错误。
- 直观的查询 API:提供简洁、直观的查询 API,使数据库操作更加易于编写和理解。
- 数据库建模:使用 Prisma Schema 定义数据库模型,支持多种数据库。
- 迁移工具:内置强大的数据库迁移工具,管理数据库模式变更。
- 数据库代理:提供数据库代理服务,优化数据库连接和查询性能。
- 支持多种数据库:支持 PostgreSQL、MySQL、SQLite、MongoDB 等多种数据库。
- 生态系统丰富:提供了丰富的工具和集成,如 Prisma Client、Prisma Migrate、Prisma Studio 等。
- 现代开发体验:与现代前端框架和工具良好集成,如 Next.js、NestJS 等。
安装与配置
安装 Prisma
使用 npm 或 yarn 安装 Prisma CLI 和 Prisma Client:
# 使用 npm 安装
npm install prisma @prisma/client
# 使用 yarn 安装
yarn add prisma @prisma/client初始化 Prisma
初始化 Prisma 项目,创建基本的配置文件:
# 初始化 Prisma
npx prisma init执行此命令后,会在项目根目录创建 .prisma 目录和 prisma/schema.prisma 文件,以及更新 .env 文件。
配置数据库连接
在 .env 文件中配置数据库连接字符串:
# PostgreSQL
DATABASE_URL="postgresql://username:password@localhost:5432/database?schema=public"
# MySQL
# DATABASE_URL="mysql://username:password@localhost:3306/database"
# SQLite
# DATABASE_URL="file:./dev.db"定义数据模型
在 prisma/schema.prisma 文件中定义数据模型:
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql" // 或 mysql、sqlite、mongodb
url = env("DATABASE_URL")
}
model User {
id Int @id @default(autoincrement())
name String
email String @unique
posts Post[]
}
model Post {
id Int @id @default(autoincrement())
title String
content String?
published Boolean @default(false)
author User @relation(fields: [authorId], references: [id])
authorId Int
}生成 Prisma Client
根据数据模型生成 Prisma Client:
# 生成 Prisma Client
npx prisma generate数据库迁移
创建数据库迁移并应用到数据库:
# 创建迁移
npx prisma migrate dev --name init核心概念
Prisma Schema
Prisma Schema 是 Prisma 的核心概念,它定义了数据库模型、关系和其他配置。
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
id Int @id @default(autoincrement())
name String
email String @unique
posts Post[]
}
model Post {
id Int @id @default(autoincrement())
title String
content String?
published Boolean @default(false)
author User @relation(fields: [authorId], references: [id])
authorId Int
}Prisma Client
Prisma Client 是一个自动生成的数据库客户端,它提供了类型安全的查询 API。
// 导入 Prisma Client
const { PrismaClient } = require('@prisma/client');
// 创建 Prisma Client 实例
const prisma = new PrismaClient();
// 使用 Prisma Client 查询数据
async function getUsers() {
const users = await prisma.user.findMany();
return users;
}数据模型
数据模型定义了数据库中的表结构和关系。
基本模型
model User {
id Int @id @default(autoincrement())
name String
email String @unique
}关系模型
model User {
id Int @id @default(autoincrement())
name String
posts Post[]
}
model Post {
id Int @id @default(autoincrement())
title String
content String?
author User @relation(fields: [authorId], references: [id])
authorId Int
}枚举类型
enum Role {
USER
ADMIN
}
model User {
id Int @id @default(autoincrement())
name String
role Role @default(USER)
}查询操作
Prisma Client 提供了丰富的查询操作,用于增删改查数据。
查询所有记录
// 查询所有用户
const users = await prisma.user.findMany();查询单个记录
// 根据 ID 查询用户
const user = await prisma.user.findUnique({
where: { id: 1 }
});条件查询
// 条件查询用户
const users = await prisma.user.findMany({
where: {
name: {
contains: 'John'
}
}
});排序和分页
// 排序和分页
const users = await prisma.user.findMany({
orderBy: {
name: 'asc'
},
skip: 0,
take: 10
});关联查询
// 关联查询
const users = await prisma.user.findMany({
include: {
posts: true
}
});创建记录
// 创建用户
const user = await prisma.user.create({
data: {
name: 'John Doe',
email: 'john@example.com'
}
});更新记录
// 更新用户
const user = await prisma.user.update({
where: { id: 1 },
data: {
name: 'John Smith'
}
});删除记录
// 删除用户
const user = await prisma.user.delete({
where: { id: 1 }
});实用案例分析
构建 RESTful API
下面是一个使用 Prisma 构建 RESTful API 的示例,实现了基本的 CRUD 操作。
项目结构
├── prisma/
│ └── schema.prisma
├── src/
│ ├── controllers/
│ │ └── userController.js
│ ├── routes/
│ │ └── userRoutes.js
│ ├── services/
│ │ └── userService.js
│ └── index.js
├── .env
└── package.json代码实现
- 数据模型 (
prisma/schema.prisma):
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
id Int @id @default(autoincrement())
name String
email String @unique
posts Post[]
}
model Post {
id Int @id @default(autoincrement())
title String
content String?
published Boolean @default(false)
author User @relation(fields: [authorId], references: [id])
authorId Int
}- 用户服务 (
src/services/userService.js):
const { PrismaClient } = require('@prisma/client');
const prisma = new PrismaClient();
// 获取所有用户
const getUsers = async () => {
return await prisma.user.findMany({
include: {
posts: true
}
});
};
// 根据 ID 获取用户
const getUserById = async (id) => {
return await prisma.user.findUnique({
where: { id: parseInt(id) },
include: {
posts: true
}
});
};
// 创建用户
const createUser = async (userData) => {
return await prisma.user.create({
data: userData
});
};
// 更新用户
const updateUser = async (id, userData) => {
return await prisma.user.update({
where: { id: parseInt(id) },
data: userData
});
};
// 删除用户
const deleteUser = async (id) => {
return await prisma.user.delete({
where: { id: parseInt(id) }
});
};
module.exports = {
getUsers,
getUserById,
createUser,
updateUser,
deleteUser
};- 用户控制器 (
src/controllers/userController.js):
const userService = require('../services/userService');
// 获取所有用户
const getUsers = async (req, res) => {
try {
const users = await userService.getUsers();
res.status(200).json(users);
} catch (error) {
res.status(500).json({ error: error.message });
}
};
// 根据 ID 获取用户
const getUserById = async (req, res) => {
try {
const user = await userService.getUserById(req.params.id);
if (!user) {
res.status(404).json({ error: 'User not found' });
return;
}
res.status(200).json(user);
} catch (error) {
res.status(500).json({ error: error.message });
}
};
// 创建用户
const createUser = async (req, res) => {
try {
const user = await userService.createUser(req.body);
res.status(201).json(user);
} catch (error) {
res.status(400).json({ error: error.message });
}
};
// 更新用户
const updateUser = async (req, res) => {
try {
const user = await userService.updateUser(req.params.id, req.body);
if (!user) {
res.status(404).json({ error: 'User not found' });
return;
}
res.status(200).json(user);
} catch (error) {
res.status(400).json({ error: error.message });
}
};
// 删除用户
const deleteUser = async (req, res) => {
try {
const user = await userService.deleteUser(req.params.id);
res.status(200).json({ message: 'User deleted successfully' });
} catch (error) {
res.status(404).json({ error: 'User not found' });
}
};
module.exports = {
getUsers,
getUserById,
createUser,
updateUser,
deleteUser
};- 用户路由 (
src/routes/userRoutes.js):
const express = require('express');
const router = express.Router();
const userController = require('../controllers/userController');
// 路由定义
router.get('/', userController.getUsers);
router.get('/:id', userController.getUserById);
router.post('/', userController.createUser);
router.put('/:id', userController.updateUser);
router.delete('/:id', userController.deleteUser);
module.exports = router;- 主应用 (
src/index.js):
const express = require('express');
const userRoutes = require('./routes/userRoutes');
const app = express();
const port = 3000;
// 中间件
app.use(express.json());
// 路由
app.use('/api/users', userRoutes);
// 健康检查
app.get('/health', (req, res) => {
res.status(200).json({ status: 'ok' });
});
// 启动服务器
app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});- 配置文件 (
package.json):
{
"name": "prisma-rest-api",
"version": "1.0.0",
"description": "RESTful API with Prisma",
"main": "src/index.js",
"scripts": {
"start": "node src/index.js",
"dev": "nodemon src/index.js"
},
"dependencies": {
"express": "^4.18.2",
"prisma": "^4.15.0",
"@prisma/client": "^4.15.0"
},
"devDependencies": {
"nodemon": "^2.0.22"
}
}- 环境变量 (
.env):
DATABASE_URL="postgresql://username:password@localhost:5432/prisma-demo?schema=public"测试 API
- 初始化数据库:
# 创建迁移
npx prisma migrate dev --name init
# 生成 Prisma Client
npx prisma generate- 启动服务器:
npm run dev- 使用 curl 或 Postman 测试 API:
# 获取所有用户
curl http://localhost:3000/api/users
# 获取单个用户
curl http://localhost:3000/api/users/1
# 创建用户
curl -X POST http://localhost:3000/api/users -H "Content-Type: application/json" -d '{"name": "John Doe", "email": "john@example.com"}'
# 更新用户
curl -X PUT http://localhost:3000/api/users/1 -H "Content-Type: application/json" -d '{"name": "John Smith"}'
# 删除用户
curl -X DELETE http://localhost:3000/api/users/1
# 健康检查
curl http://localhost:3000/health高级特性
事务
Prisma 支持数据库事务,确保多个操作的原子性:
// 使用事务
const result = await prisma.$transaction(async (prisma) => {
// 创建用户
const user = await prisma.user.create({
data: {
name: 'John Doe',
email: 'john@example.com'
}
});
// 创建帖子
const post = await prisma.post.create({
data: {
title: 'First Post',
content: 'Hello World!',
authorId: user.id
}
});
return { user, post };
});批量操作
Prisma 支持批量创建、更新和删除操作:
// 批量创建用户
const users = await prisma.user.createMany({
data: [
{ name: 'John Doe', email: 'john@example.com' },
{ name: 'Jane Doe', email: 'jane@example.com' }
]
});
// 批量更新用户
const updatedUsers = await prisma.user.updateMany({
where: {
name: {
contains: 'Doe'
}
},
data: {
name: {
append: ' Smith'
}
}
});
// 批量删除用户
const deletedUsers = await prisma.user.deleteMany({
where: {
email: {
endsWith: '@example.com'
}
}
});原生 SQL 查询
对于复杂的查询,Prisma 支持执行原生 SQL 查询:
// 执行原生 SQL 查询
const users = await prisma.$queryRaw`SELECT * FROM User WHERE name = ${name}`;
// 执行参数化原生 SQL 查询
const users = await prisma.$queryRaw(
Prisma.sql`SELECT * FROM User WHERE name = ${name}`
);Prisma Studio
Prisma Studio 是一个可视化的数据库管理工具,用于查看和编辑数据库数据:
# 启动 Prisma Studio
npx prisma studio执行此命令后,会在浏览器中打开 Prisma Studio 界面,可以直观地查看和编辑数据库数据。
性能优化
1. 使用连接池
Prisma 默认使用连接池来管理数据库连接,提高查询性能:
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
shadowDatabaseUrl = env("SHADOW_DATABASE_URL") // 用于迁移
}2. 选择字段
只选择需要的字段,减少数据传输量:
// 不推荐:选择所有字段
const users = await prisma.user.findMany();
// 推荐:只选择需要的字段
const users = await prisma.user.findMany({
select: {
id: true,
name: true
}
});3. 批量查询
使用批量查询减少数据库请求次数:
// 不推荐:多次查询
const user = await prisma.user.findUnique({ where: { id: 1 } });
const posts = await prisma.post.findMany({ where: { authorId: 1 } });
// 推荐:单次查询
const user = await prisma.user.findUnique({
where: { id: 1 },
include: {
posts: true
}
});4. 索引优化
在 Prisma Schema 中定义索引,提高查询性能:
model User {
id Int @id @default(autoincrement())
name String
email String @unique // 自动创建唯一索引
posts Post[]
@@index([name]) // 创建普通索引
}5. 使用缓存
对于频繁访问的数据,使用缓存来减少数据库查询:
const Redis = require('redis');
const client = Redis.createClient();
// 缓存用户数据
async function getUserWithCache(id) {
const cacheKey = `user:${id}`;
// 尝试从缓存获取
const cachedUser = await client.get(cacheKey);
if (cachedUser) {
return JSON.parse(cachedUser);
}
// 从数据库获取
const user = await prisma.user.findUnique({ where: { id: parseInt(id) } });
// 存入缓存
if (user) {
await client.set(cacheKey, JSON.stringify(user), { EX: 3600 });
}
return user;
}6. 分页查询
对于大量数据,使用分页查询避免一次性加载过多数据:
// 分页查询
const users = await prisma.user.findMany({
skip: (page - 1) * pageSize,
take: pageSize,
orderBy: {
createdAt: 'desc'
}
});总结
Prisma 是一个现代、强大的 ORM 工具,它通过提供类型安全、直观的查询 API,使数据库访问变得更加简单、可靠和高效。Prisma 的核心特性包括类型安全、直观的查询 API、数据库建模、迁移工具、数据库代理等,这些特性使得它成为构建现代应用的理想选择。
通过本教程,你应该已经了解了 Prisma 的核心概念和基本用法,包括安装配置、数据模型定义、查询操作、关系管理等,以及如何使用 Prisma 构建 RESTful API。你还学习了 Prisma 的高级特性,如事务、批量操作、原生 SQL 查询、Prisma Studio 等,以及一些性能优化的方法。
Prisma 的设计理念和功能使其成为 Node.js 和 TypeScript 项目中处理数据库操作的优秀选择,它的生态系统也在不断发展,提供了更多的工具和集成,以满足各种应用场景的需求。要深入学习 Prisma,建议查阅 官方文档 和实践更多的项目案例,以掌握其高级特性和最佳实践。