Prisma 教程 - 现代的 Node.js 和 TypeScript ORM

一、项目概述

Prisma 是一个现代的 Node.js 和 TypeScript ORM(对象关系映射)工具,专为 Node.js 和 TypeScript 应用设计。它提供了一种类型安全、直观的方式来访问数据库,使数据库操作变得更加简单和可靠。Prisma 的核心设计理念是通过自动生成的类型定义,为开发者提供类型安全的数据库访问体验。

1.1 核心概念

  • 数据模型:Prisma 的数据模型定义,使用 Prisma Schema Language (PSL) 编写
  • Prisma Client:自动生成的类型安全的数据库客户端
  • Prisma Migrate:数据库迁移工具,用于管理数据库模式变更
  • Prisma Studio:可视化数据库管理工具
  • Prisma Schema:定义数据模型、数据源和生成配置的文件

1.2 核心特点

  • 类型安全:自动生成的 TypeScript 类型,提供编译时类型检查
  • 直观的 API:链式调用的查询 API,代码更易读
  • 自动迁移:内置的数据库迁移工具,简化模式变更
  • 多数据库支持:支持 PostgreSQL、MySQL、SQLite、MongoDB 等
  • 实时反馈:开发时的实时类型检查和错误提示
  • 可视化工具:Prisma Studio 提供可视化数据库管理
  • 生态系统集成:与 Express、Next.js、NestJS 等框架集成良好

二、安装与设置

2.1 安装方式

通过 npm 安装:

npm install prisma @prisma/client

通过 yarn 安装:

yarn add prisma @prisma/client

2.2 基本设置

初始化 Prisma:

# 初始化 Prisma
npx prisma init

配置数据源:

// prisma/schema.prisma
generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "postgresql" // 可选:postgresql, mysql, sqlite, mongodb
  url      = env("DATABASE_URL")
}

设置环境变量:

# .env
DATABASE_URL="postgresql://username:password@localhost:5432/mydb?schema=public"

2.3 生成 Prisma Client

生成 Prisma Client:

npx prisma generate

使用 Prisma Client:

// src/prisma.js
const { PrismaClient } = require('@prisma/client');

const prisma = new PrismaClient();

module.exports = prisma;
// src/prisma.ts
import { PrismaClient } from '@prisma/client';

const prisma = new PrismaClient();

export default prisma;

三、基础用法

3.1 定义数据模型

基本数据模型:

// 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[]
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}

model Post {
  id        Int      @id @default(autoincrement())
  title     String
  content   String?
  published Boolean  @default(false)
  authorId  Int
  author    User     @relation(fields: [authorId], references: [id])
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}

数据模型字段类型:

类型 描述 示例
String 字符串 name String
Int 整数 age Int
Boolean 布尔值 published Boolean
Float 浮点数 price Float
DateTime 日期时间 createdAt DateTime
Json JSON 数据 metadata Json
Bytes 二进制数据 avatar Bytes
Decimal 十进制数 amount Decimal

数据模型属性:

属性 描述 示例
@id 主键 id Int @id
@unique 唯一约束 email String @unique
@default 默认值 createdAt DateTime @default(now())
@updatedAt 自动更新时间戳 updatedAt DateTime @updatedAt
@relation 关系 author User @relation(fields: [authorId], references: [id])
@map 映射到数据库字段 name String @map("full_name")
@db.VarChar(255) 数据库特定类型 name String @db.VarChar(255)

3.2 数据库迁移

创建迁移:

# 创建新迁移
npx prisma migrate dev --name init

# 应用迁移到生产环境
npx prisma migrate deploy

# 生成 SQL 而不执行
npx prisma migrate diff --from-empty --to-schema-datamodel prisma/schema.prisma --script > migration.sql

重置数据库:

# 重置数据库(删除所有数据和表,重新应用迁移)
npx prisma migrate reset

3.3 基本查询

创建记录:

// 创建用户
const user = await prisma.user.create({
  data: {
    name: '张三',
    email: 'zhangsan@example.com'
  }
});

// 创建带关联的记录
const post = await prisma.post.create({
  data: {
    title: 'Hello Prisma',
    content: 'Prisma 是一个现代的 ORM',
    published: true,
    author: {
      connect: { id: 1 }
    }
  }
});

// 同时创建用户和帖子
const userWithPost = await prisma.user.create({
  data: {
    name: '李四',
    email: 'lisi@example.com',
    posts: {
      create: {
        title: 'First Post',
        content: 'This is my first post',
        published: true
      }
    }
  }
});

读取记录:

// 获取所有用户
const users = await prisma.user.findMany();

// 获取单个用户
const user = await prisma.user.findUnique({
  where: { id: 1 }
});

// 带条件查询
const publishedPosts = await prisma.post.findMany({
  where: {
    published: true
  }
});

// 带排序和分页
const posts = await prisma.post.findMany({
  where: {
    authorId: 1
  },
  orderBy: {
    createdAt: 'desc'
  },
  skip: 0,
  take: 10
});

// 带关联查询
const userWithPosts = await prisma.user.findUnique({
  where: { id: 1 },
  include: {
    posts: true
  }
});

// 带条件的关联查询
const userWithPublishedPosts = await prisma.user.findUnique({
  where: { id: 1 },
  include: {
    posts: {
      where: {
        published: true
      }
    }
  }
});

更新记录:

// 更新用户
const updatedUser = await prisma.user.update({
  where: { id: 1 },
  data: {
    name: '张三(更新)'
  }
});

// 更新带条件
const updatedPosts = await prisma.post.updateMany({
  where: {
    authorId: 1,
    published: false
  },
  data: {
    published: true
  }
});

// 关联更新
const user = await prisma.user.update({
  where: { id: 1 },
  data: {
    posts: {
      create: {
        title: 'New Post',
        content: 'This is a new post',
        published: true
      }
    }
  },
  include: {
    posts: true
  }
});

删除记录:

// 删除用户
const deletedUser = await prisma.user.delete({
  where: { id: 1 }
});

// 删除带条件
const deletedPosts = await prisma.post.deleteMany({
  where: {
    authorId: 1,
    published: false
  }
});

// 删除关联记录
const user = await prisma.user.update({
  where: { id: 1 },
  data: {
    posts: {
      deleteMany: {
        published: false
      }
    }
  },
  include: {
    posts: true
  }
});

3.4 高级查询

聚合查询:

// 计数
const postCount = await prisma.post.count({
  where: {
    published: true
  }
});

// 分组计数
const postsByAuthor = await prisma.post.groupBy({
  by: ['authorId'],
  _count: {
    id: true
  },
  orderBy: {
    _count: {
      id: 'desc'
    }
  }
});

// 平均值、总和、最小值、最大值
const stats = await prisma.post.aggregate({
  where: {
    published: true
  },
  _avg: {
    id: true
  },
  _sum: {
    id: true
  },
  _min: {
    id: true
  },
  _max: {
    id: true
  }
});

复杂条件查询:

// 复杂条件
const posts = await prisma.post.findMany({
  where: {
    OR: [
      {
        title: {
          contains: 'Prisma'
        }
      },
      {
        content: {
          contains: 'ORM'
        }
      }
    ],
    AND: [
      {
        published: true
      },
      {
        createdAt: {
          gte: new Date('2023-01-01')
        }
      }
    ]
  }
});

// 嵌套条件
const users = await prisma.user.findMany({
  where: {
    posts: {
      some: {
        published: true,
        title: {
          contains: 'Prisma'
        }
      }
    }
  },
  include: {
    posts: true
  }
});

关系查询:

// 一对一关系
const user = await prisma.user.findUnique({
  where: { id: 1 },
  include: {
    profile: true
  }
});

// 一对多关系
const userWithPosts = await prisma.user.findUnique({
  where: { id: 1 },
  include: {
    posts: {
      where: {
        published: true
      },
      orderBy: {
        createdAt: 'desc'
      }
    }
  }
});

// 多对多关系
const postWithTags = await prisma.post.findUnique({
  where: { id: 1 },
  include: {
    tags: true
  }
});

// 嵌套关系
const userWithPostsAndComments = await prisma.user.findUnique({
  where: { id: 1 },
  include: {
    posts: {
      include: {
        comments: true
      }
    }
  }
});

四、高级特性

4.1 事务

基本事务:

// 基本事务
const [user, post] = await prisma.$transaction([
  prisma.user.create({
    data: {
      name: '王五',
      email: 'wangwu@example.com'
    }
  }),
  prisma.post.create({
    data: {
      title: 'Transaction Post',
      content: 'This post was created in a transaction',
      published: true,
      author: {
        connect: { email: 'wangwu@example.com' }
      }
    }
  })
]);

// 交互式事务
const result = await prisma.$transaction(async (prisma) => {
  // 创建用户
  const user = await prisma.user.create({
    data: {
      name: '赵六',
      email: 'zhaoliu@example.com'
    }
  });

  // 创建帖子
  const post = await prisma.post.create({
    data: {
      title: 'Interactive Transaction Post',
      content: 'This post was created in an interactive transaction',
      published: true,
      authorId: user.id
    }
  });

  // 验证帖子是否创建成功
  const createdPost = await prisma.post.findUnique({
    where: { id: post.id }
  });

  if (!createdPost) {
    throw new Error('Post creation failed');
  }

  return { user, post };
});

4.2 批量操作

批量创建:

// 批量创建用户
const users = await prisma.user.createMany({
  data: [
    { name: '用户1', email: 'user1@example.com' },
    { name: '用户2', email: 'user2@example.com' },
    { name: '用户3', email: 'user3@example.com' }
  ],
  skipDuplicates: true // 跳过重复记录
});

// 批量更新
const updatedPosts = await prisma.post.updateMany({
  where: {
    authorId: 1
  },
  data: {
    published: true
  }
});

// 批量删除
const deletedPosts = await prisma.post.deleteMany({
  where: {
    authorId: 1,
    published: false
  }
});

4.3 原生查询

执行原生 SQL:

// 执行原生 SQL
const users = await prisma.$queryRaw`SELECT * FROM "User" WHERE "name" LIKE ${'%张%'}`;

// 执行参数化查询
const posts = await prisma.$queryRawPrisma(
  Prisma.sql`SELECT * FROM "Post" WHERE "published" = ${true} ORDER BY "createdAt" DESC`
);

// 执行更新操作
const result = await prisma.$executeRaw`UPDATE "Post" SET "published" = ${true} WHERE "authorId" = ${1}`;

4.4 索引和性能优化

添加索引:

// prisma/schema.prisma
model Post {
  id        Int      @id @default(autoincrement())
  title     String   @unique
  content   String?
  published Boolean  @default(false)
  authorId  Int
  author    User     @relation(fields: [authorId], references: [id])
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  // 添加索引
  @@index([authorId, published])
  @@index([createdAt])
}

// 复合唯一索引
model User {
  id        Int      @id @default(autoincrement())
  name      String
  email     String   @unique
  posts     Post[]
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  @@unique([name, email])
}

性能优化建议:

  1. 使用索引:为频繁查询的字段添加索引
  2. 选择特定字段:使用 select 只获取需要的字段
  3. 分页查询:使用 skiptake 进行分页
  4. 批量操作:使用 createManyupdateManydeleteMany 进行批量操作
  5. 避免 N+1 查询:使用 include 预加载关联数据
  6. 使用事务:对于多个相关操作,使用事务确保原子性
  7. 监控查询性能:使用 Prisma 的日志功能监控查询性能

4.5 中间件

Prisma 中间件:

// 添加中间件
prisma.$use(async (params, next) => {
  const before = Date.now();
  
  const result = await next(params);
  
  const after = Date.now();
  console.log(`Query ${params.model}.${params.action} took ${after - before}ms`);
  
  return result;
});

// 日志中间件
prisma.$use(async (params, next) => {
  console.log(`Starting query ${params.model}.${params.action}`);
  console.log('Query params:', params.args);
  
  const result = await next(params);
  
  console.log(`Query ${params.model}.${params.action} completed`);
  return result;
});

// 软删除中间件
prisma.$use(async (params, next) => {
  if (params.model === 'Post' && params.action === 'delete') {
    // 转换为更新操作
    params.action = 'update';
    params.args.data = {
      ...params.args.data,
      deletedAt: new Date()
    };
  }
  
  if (params.model === 'Post' && params.action === 'findMany') {
    // 添加软删除过滤
    params.args.where = {
      ...params.args.where,
      deletedAt: null
    };
  }
  
  return next(params);
});

五、实际应用场景

5.1 构建 RESTful API

使用 Express 和 Prisma 构建 RESTful API:

# 安装依赖
npm install express prisma @prisma/client
npx prisma init

定义数据模型:

// 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[]
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}

model Post {
  id        Int      @id @default(autoincrement())
  title     String
  content   String?
  published Boolean  @default(false)
  authorId  Int
  author    User     @relation(fields: [authorId], references: [id])
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}

生成 Prisma Client 并创建迁移:

npx prisma migrate dev --name init
npx prisma generate

创建 Express 应用:

// src/index.js
const express = require('express');
const prisma = require('./prisma');

const app = express();
app.use(express.json());

// 获取所有用户
app.get('/users', async (req, res) => {
  const users = await prisma.user.findMany({
    include: {
      posts: true
    }
  });
  res.json(users);
});

// 获取单个用户
app.get('/users/:id', async (req, res) => {
  const { id } = req.params;
  const user = await prisma.user.findUnique({
    where: { id: parseInt(id) },
    include: {
      posts: true
    }
  });
  if (user) {
    res.json(user);
  } else {
    res.status(404).json({ error: 'User not found' });
  }
});

// 创建用户
app.post('/users', async (req, res) => {
  const user = await prisma.user.create({
    data: req.body
  });
  res.status(201).json(user);
});

// 更新用户
app.put('/users/:id', async (req, res) => {
  const { id } = req.params;
  const user = await prisma.user.update({
    where: { id: parseInt(id) },
    data: req.body
  });
  res.json(user);
});

// 删除用户
app.delete('/users/:id', async (req, res) => {
  const { id } = req.params;
  const user = await prisma.user.delete({
    where: { id: parseInt(id) }
  });
  res.json(user);
});

// 获取所有帖子
app.get('/posts', async (req, res) => {
  const posts = await prisma.post.findMany({
    include: {
      author: true
    }
  });
  res.json(posts);
});

// 创建帖子
app.post('/posts', async (req, res) => {
  const post = await prisma.post.create({
    data: req.body
  });
  res.status(201).json(post);
});

// 启动服务器
app.listen(3000, () => {
  console.log('Server running on http://localhost:3000');
});

创建 Prisma 实例:

// src/prisma.js
const { PrismaClient } = require('@prisma/client');

const prisma = new PrismaClient();

module.exports = prisma;

5.2 构建 GraphQL API

使用 Apollo Server 和 Prisma 构建 GraphQL API:

# 安装依赖
npm install apollo-server prisma @prisma/client graphql
npx prisma init

定义数据模型:

// 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[]
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}

model Post {
  id        Int      @id @default(autoincrement())
  title     String
  content   String?
  published Boolean  @default(false)
  authorId  Int
  author    User     @relation(fields: [authorId], references: [id])
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}

生成 Prisma Client 并创建迁移:

npx prisma migrate dev --name init
npx prisma generate

创建 GraphQL schema:

# src/schema.graphql
type User {
  id: ID!
  name: String!
  email: String!
  posts: [Post!]!
  createdAt: String!
  updatedAt: String!
}

type Post {
  id: ID!
  title: String!
  content: String
  published: Boolean!
  author: User!
  createdAt: String!
  updatedAt: String!
}

type Query {
  users: [User!]!
  user(id: ID!): User
  posts: [Post!]!
  post(id: ID!): Post
}

type Mutation {
  createUser(name: String!, email: String!): User!
  updateUser(id: ID!, name: String, email: String): User!
  deleteUser(id: ID!): User!
  createPost(title: String!, content: String, published: Boolean!, authorId: ID!): Post!
  updatePost(id: ID!, title: String, content: String, published: Boolean): Post!
  deletePost(id: ID!): Post!
}

创建 Apollo Server:

// src/index.js
const { ApolloServer, gql } = require('apollo-server');
const { PrismaClient } = require('@prisma/client');
const fs = require('fs');
const path = require('path');

const prisma = new PrismaClient();

// 读取 GraphQL schema
const typeDefs = gql(fs.readFileSync(path.join(__dirname, 'schema.graphql'), 'utf8'));

// 定义 resolvers
const resolvers = {
  Query: {
    users: async () => {
      return await prisma.user.findMany({ include: { posts: true } });
    },
    user: async (parent, { id }) => {
      return await prisma.user.findUnique({ 
        where: { id: parseInt(id) },
        include: { posts: true }
      });
    },
    posts: async () => {
      return await prisma.post.findMany({ include: { author: true } });
    },
    post: async (parent, { id }) => {
      return await prisma.post.findUnique({ 
        where: { id: parseInt(id) },
        include: { author: true }
      });
    },
  },
  Mutation: {
    createUser: async (parent, { name, email }) => {
      return await prisma.user.create({ data: { name, email } });
    },
    updateUser: async (parent, { id, name, email }) => {
      return await prisma.user.update({ 
        where: { id: parseInt(id) },
        data: { name, email }
      });
    },
    deleteUser: async (parent, { id }) => {
      return await prisma.user.delete({ where: { id: parseInt(id) } });
    },
    createPost: async (parent, { title, content, published, authorId }) => {
      return await prisma.post.create({ 
        data: { 
          title, 
          content, 
          published, 
          authorId: parseInt(authorId) 
        },
        include: { author: true }
      });
    },
    updatePost: async (parent, { id, title, content, published }) => {
      return await prisma.post.update({ 
        where: { id: parseInt(id) },
        data: { title, content, published },
        include: { author: true }
      });
    },
    deletePost: async (parent, { id }) => {
      return await prisma.post.delete({ where: { id: parseInt(id) } });
    },
  },
};

// 创建 Apollo Server
const server = new ApolloServer({ typeDefs, resolvers });

// 启动服务器
server.listen().then(({ url }) => {
  console.log(`Server running at ${url}`);
});

5.3 构建 Next.js 应用

使用 Next.js 和 Prisma 构建全栈应用:

# 创建 Next.js 应用
npx create-next-app@latest my-app
cd my-app

# 安装依赖
npm install prisma @prisma/client

# 初始化 Prisma
npx prisma init

定义数据模型:

// 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[]
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}

model Post {
  id        Int      @id @default(autoincrement())
  title     String
  content   String?
  published Boolean  @default(false)
  authorId  Int
  author    User     @relation(fields: [authorId], references: [id])
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}

生成 Prisma Client 并创建迁移:

npx prisma migrate dev --name init
npx prisma generate

创建 Prisma 实例:

// lib/prisma.js
import { PrismaClient } from '@prisma/client';

let prisma;

if (process.env.NODE_ENV === 'production') {
  prisma = new PrismaClient();
} else {
  if (!global.prisma) {
    global.prisma = new PrismaClient();
  }
  prisma = global.prisma;
}

export default prisma;

创建 API 路由:

// pages/api/users.js
import prisma from '../../lib/prisma';

export default async function handler(req, res) {
  const { method } = req;

  switch (method) {
    case 'GET':
      try {
        const users = await prisma.user.findMany({ include: { posts: true } });
        res.status(200).json(users);
      } catch (error) {
        res.status(500).json({ error: error.message });
      }
      break;
    case 'POST':
      try {
        const user = await prisma.user.create({ data: req.body });
        res.status(201).json(user);
      } catch (error) {
        res.status(500).json({ error: error.message });
      }
      break;
    default:
      res.setHeader('Allow', ['GET', 'POST']);
      res.status(405).end(`Method ${method} Not Allowed`);
  }
}
// pages/api/posts.js
import prisma from '../../lib/prisma';

export default async function handler(req, res) {
  const { method } = req;

  switch (method) {
    case 'GET':
      try {
        const posts = await prisma.post.findMany({ include: { author: true } });
        res.status(200).json(posts);
      } catch (error) {
        res.status(500).json({ error: error.message });
      }
      break;
    case 'POST':
      try {
        const post = await prisma.post.create({ data: req.body });
        res.status(201).json(post);
      } catch (error) {
        res.status(500).json({ error: error.message });
      }
      break;
    default:
      res.setHeader('Allow', ['GET', 'POST']);
      res.status(405).end(`Method ${method} Not Allowed`);
  }
}

创建页面:

// pages/index.js
import { useState, useEffect } from 'react';

export default function Home() {
  const [users, setUsers] = useState([]);
  const [posts, setPosts] = useState([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    async function fetchData() {
      const [usersRes, postsRes] = await Promise.all([
        fetch('/api/users'),
        fetch('/api/posts')
      ]);
      const usersData = await usersRes.json();
      const postsData = await postsRes.json();
      setUsers(usersData);
      setPosts(postsData);
      setLoading(false);
    }
    fetchData();
  }, []);

  if (loading) {
    return <div>Loading...</div>;
  }

  return (
    <div className="container mx-auto px-4 py-8">
      <h1 className="text-3xl font-bold mb-8">Prisma Next.js Example</h1>
      
      <div className="mb-8">
        <h2 className="text-2xl font-semibold mb-4">Users</h2>
        <ul className="list-disc pl-6">
          {users.map(user => (
            <li key={user.id} className="mb-2">
              {user.name} ({user.email})
              <ul className="list-circle pl-6 mt-1">
                {user.posts.map(post => (
                  <li key={post.id}>{post.title}</li>
                ))}
              </ul>
            </li>
          ))}
        </ul>
      </div>
      
      <div>
        <h2 className="text-2xl font-semibold mb-4">Posts</h2>
        <ul className="list-disc pl-6">
          {posts.map(post => (
            <li key={post.id} className="mb-2">
              {post.title} by {post.author.name}
            </li>
          ))}
        </ul>
      </div>
    </div>
  );
}

六、性能优化建议

6.1 查询优化

  1. 选择特定字段:使用 select 只获取需要的字段
// 好的做法:只选择需要的字段
const users = await prisma.user.findMany({
  select: {
    id: true,
    name: true,
    email: true
  }
});

// 不好的做法:获取所有字段
const users = await prisma.user.findMany();
  1. 使用索引:为频繁查询的字段添加索引
// 添加索引
model User {
  id        Int      @id @default(autoincrement())
  name      String
  email     String   @unique
  posts     Post[]
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  @@index([createdAt])
}
  1. 分页查询:使用 skiptake 进行分页
// 分页查询
const posts = await prisma.post.findMany({
  skip: (page - 1) * pageSize,
  take: pageSize,
  orderBy: {
    createdAt: 'desc'
  }
});
  1. 避免 N+1 查询:使用 include 预加载关联数据
// 好的做法:使用 include 预加载
const users = await prisma.user.findMany({
  include: {
    posts: true
  }
});

// 不好的做法:多次查询
const users = await prisma.user.findMany();
for (const user of users) {
  user.posts = await prisma.post.findMany({ where: { authorId: user.id } });
}
  1. 批量操作:使用批量操作减少数据库请求
// 好的做法:使用 createMany
const result = await prisma.user.createMany({
  data: [
    { name: '用户1', email: 'user1@example.com' },
    { name: '用户2', email: 'user2@example.com' }
  ]
});

// 不好的做法:多次创建
for (const userData of usersData) {
  await prisma.user.create({ data: userData });
}

6.2 连接管理

  1. 使用单例模式:在应用中使用单个 Prisma Client 实例
// 单例模式
import { PrismaClient } from '@prisma/client';

let prisma;

if (process.env.NODE_ENV === 'production') {
  prisma = new PrismaClient();
} else {
  if (!global.prisma) {
    global.prisma = new PrismaClient();
  }
  prisma = global.prisma;
}

export default prisma;
  1. 关闭连接:在应用退出时关闭 Prisma Client 连接
// 关闭连接
process.on('SIGINT', async () => {
  await prisma.$disconnect();
  process.exit(0);
});

process.on('SIGTERM', async () => {
  await prisma.$disconnect();
  process.exit(0);
});

6.3 事务和并发

  1. 使用事务:对于多个相关操作,使用事务确保原子性
// 使用事务
const result = await prisma.$transaction(async (prisma) => {
  const user = await prisma.user.create({ data: userData });
  const post = await prisma.post.create({ 
    data: { ...postData, authorId: user.id } 
  });
  return { user, post };
});
  1. 处理并发:使用乐观锁或悲观锁处理并发更新
// 添加版本字段
model Post {
  id        Int      @id @default(autoincrement())
  title     String
  content   String?
  version   Int      @default(1)
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  @@unique([id, version])
}
// 乐观锁更新
async function updatePost(id, data) {
  const post = await prisma.post.findUnique({ where: { id } });
  try {
    return await prisma.post.update({
      where: { id_version: { id, version: post.version } },
      data: {
        ...data,
        version: post.version + 1
      }
    });
  } catch (error) {
    throw new Error('Concurrent update detected');
  }
}

七、常见问题与解决方案

7.1 数据库连接问题

问题:无法连接到数据库

解决方案

  • 检查数据库 URL 是否正确
  • 确保数据库服务正在运行
  • 检查数据库用户权限
  • 检查网络连接

示例:

# 正确的数据库 URL 格式
DATABASE_URL="postgresql://username:password@localhost:5432/mydb?schema=public"
DATABASE_URL="mysql://username:password@localhost:3306/mydb"
DATABASE_URL="sqlite://./dev.db"

7.2 迁移问题

问题:迁移失败

解决方案

  • 检查数据模型定义是否正确
  • 确保数据库连接正常
  • 检查是否有未解决的迁移冲突
  • 使用 prisma migrate reset 重置数据库(注意:会删除所有数据)

示例:

# 查看迁移状态
npx prisma migrate status

# 重置数据库
npx prisma migrate reset

# 创建新迁移
npx prisma migrate dev --name fix-issue

7.3 类型错误

问题:TypeScript 类型错误

解决方案

  • 重新生成 Prisma Client:npx prisma generate
  • 检查数据模型定义是否正确
  • 确保 TypeScript 配置正确
  • 检查导入路径是否正确

示例:

# 重新生成 Prisma Client
npx prisma generate

# 检查 TypeScript 类型
npx tsc --noEmit

7.4 性能问题

问题:查询性能差

解决方案

  • 添加索引
  • 优化查询(使用 selectinclude 等)
  • 监控查询性能
  • 使用缓存

示例:

# 启用查询日志
npx prisma db push --preview-feature

# 或在代码中启用
const prisma = new PrismaClient({
  log: ['query', 'info', 'warn', 'error']
});

八、Prisma 与其他 ORM 的比较

8.1 Prisma vs Sequelize

特性 Prisma Sequelize
类型安全 自动生成 TypeScript 类型 需要手动定义类型
API 风格 链式调用,更直观 基于模型方法
迁移工具 内置的 Prisma Migrate 内置的迁移工具
数据库支持 PostgreSQL、MySQL、SQLite、MongoDB 支持更多数据库
学习曲线 中等 中等
性能 中等
生态系统 不断增长 成熟

8.2 Prisma vs TypeORM

特性 Prisma TypeORM
类型安全 自动生成 TypeScript 类型 基于装饰器的类型定义
API 风格 链式调用,更直观 基于 Repository 模式
迁移工具 内置的 Prisma Migrate 内置的迁移工具
数据库支持 PostgreSQL、MySQL、SQLite、MongoDB 支持更多数据库
学习曲线 中等 较高
性能 中等
生态系统 不断增长 成熟

8.3 Prisma vs Mongoose

特性 Prisma Mongoose
类型安全 自动生成 TypeScript 类型 需要手动定义类型
数据模型 使用 Prisma Schema Language 使用 Mongoose Schema
数据库支持 支持多种数据库 仅支持 MongoDB
查询 API 链式调用,更直观 基于 MongoDB 查询语法
迁移工具 内置的 Prisma Migrate 无内置迁移工具
学习曲线 中等 中等
性能
生态系统 不断增长 成熟

九、参考资源

9.1 官方资源

9.2 学习资源

9.3 工具与扩展

官方工具:

第三方工具:

十、总结

Prisma 是一个现代的 Node.js 和 TypeScript ORM,它通过自动生成的类型定义和直观的 API,为开发者提供了类型安全、高效的数据库访问体验。Prisma 的核心优势在于其类型安全的查询 API、内置的迁移工具和可视化管理工具,使数据库操作变得更加简单和可靠。

Prisma 的核心优势在于:

  1. 类型安全:自动生成的 TypeScript 类型,提供编译时类型检查
  2. 直观的 API:链式调用的查询 API,代码更易读
  3. 自动迁移:内置的数据库迁移工具,简化模式变更
  4. 多数据库支持:支持 PostgreSQL、MySQL、SQLite、MongoDB 等
  5. 实时反馈:开发时的实时类型检查和错误提示
  6. 可视化工具:Prisma Studio 提供可视化数据库管理
  7. 生态系统集成:与 Express、Next.js、NestJS 等框架集成良好

Prisma 适合开发各种类型的应用,包括:

  • RESTful API:使用 Express、Fastify 等框架构建
  • GraphQL API:使用 Apollo Server、GraphQL Yoga 等构建
  • 全栈应用:使用 Next.js、Remix 等框架构建
  • 微服务:作为服务的数据库访问层

通过本教程的学习,你应该已经掌握了 Prisma 的基本使用方法和高级特性,可以开始在项目中应用它来简化数据库操作了。随着实践经验的积累,你会发现 Prisma 不仅是一个强大的 ORM 工具,更是一种现代的数据库访问方式,它可以帮助你编写更安全、更高效的数据库代码。

Prisma 的生态系统在不断发展壮大,新的功能和工具不断涌现。作为一名开发者,保持学习的态度,关注 Prisma 的最新发展,将会使你在数据库操作方面更加得心应手。

« 上一篇 Koa 教程 - Express 团队开发的下一代 Web 框架 下一篇 » Mongoose 教程 - MongoDB 的对象建模工具