正在加载,请稍候…

Prisma ORM:TypeScript 中的类型安全数据库访问

使用 Prisma 在 TypeScript 中实现类型安全的数据库访问。学习模式设计、迁移、关系、事务、原始查询和性能优化。

Prisma ORM:TypeScript 中的类型安全数据库访问

Prisma ORM:TypeScript 中的类型安全数据库访问

设置与模式

npm install prisma @prisma/client
npx prisma init
// prisma/schema.prisma
datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

generator client {
  provider = "prisma-client-js"
}

model User {
  id        String    @id @default(cuid())
  email     String    @unique
  name      String?
  role      Role      @default(USER)
  posts     Post[]
  profile   Profile?
  createdAt DateTime  @default(now())
  updatedAt DateTime  @updatedAt

  @@index([email])
  @@map("users")  // 表名
}

model Post {
  id          String    @id @default(cuid())
  title       String
  content     String?
  published   Boolean   @default(false)
  viewCount   Int       @default(0)
  author      User      @relation(fields: [authorId], references: [id], onDelete: Cascade)
  authorId    String
  tags        Tag[]
  publishedAt DateTime?
  createdAt   DateTime  @default(now())

  @@index([authorId])
  @@index([published, createdAt])
}

model Tag {
  id    String @id @default(cuid())
  name  String @unique
  posts Post[]
}

enum Role {
  USER
  ADMIN
  MODERATOR
}

Prisma ORM:TypeScript 中的类型安全数据库访问 插图

迁移

# 创建迁移
npx prisma migrate dev --name add-user-profile

# 在生产环境应用
npx prisma migrate deploy

# 生成客户端
npx prisma generate

Prisma ORM:TypeScript 中的类型安全数据库访问 插图

CRUD 操作

import { PrismaClient } from '@prisma/client';

const prisma = new PrismaClient({
  log: ['query', 'error', 'warn'],
});

// 创建并关联
const user = await prisma.user.create({
  data: {
    email: 'alice@example.com',
    name: 'Alice',
    posts: {
      create: [
        { title: 'My first post' },
        { title: 'Hello World', published: true },
      ],
    },
  },
  include: { posts: true },
});

// 带过滤和关联的查询
const publishedPosts = await prisma.post.findMany({
  where: {
    published: true,
    author: { role: 'ADMIN' },
    createdAt: { gte: new Date('2024-01-01') },
  },
  include: {
    author: { select: { name: true, email: true } },
    tags: true,
  },
  orderBy: { createdAt: 'desc' },
  skip: 0,
  take: 20,
});

// 更新
await prisma.post.update({
  where: { id: 'post_123' },
  data: {
    published: true,
    publishedAt: new Date(),
    viewCount: { increment: 1 },
  },
});

// 更新或创建
await prisma.user.upsert({
  where: { email: 'alice@example.com' },
  create: { email: 'alice@example.com', name: 'Alice' },
  update: { name: 'Alice Updated' },
});

// 级联删除
await prisma.user.delete({ where: { id: userId } });

Prisma ORM:TypeScript 中的类型安全数据库访问 插图

事务

// 交互式事务
const result = await prisma.$transaction(async (tx) => {
  const user = await tx.user.create({ data: { email: 'bob@example.com' } });
  const post = await tx.post.create({
    data: { title: 'First Post', authorId: user.id },
  });
  return { user, post };
});

// 顺序事务(更简单)
const [usersCount, postsCount] = await prisma.$transaction([
  prisma.user.count(),
  prisma.post.count({ where: { published: true } }),
]);

原始查询

// 当 Prisma API 不够用时使用原始 SQL
const users = await prisma.$queryRaw<User[]>`
  SELECT u.*, COUNT(p.id)::int as post_count
  FROM users u
  LEFT JOIN posts p ON p.author_id = u.id
  GROUP BY u.id
  ORDER BY post_count DESC
  LIMIT 10
`;

// 执行原始查询(无返回值)
await prisma.$executeRaw`
  UPDATE posts SET view_count = view_count + 1
  WHERE id = ${postId}
`;

中间件与软删除

prisma.$use(async (params, next) => {
  // 软删除中间件
  if (params.model === 'Post') {
    if (params.action === 'delete') {
      params.action = 'update';
      params.args['data'] = { deletedAt: new Date() };
    }
    if (params.action === 'findMany') {
      params.args.where = { ...params.args.where, deletedAt: null };
    }
  }
  return next(params);
});

Prisma 的类型安全在编译时捕获模式变更,防止运行时数据库错误。