全书目录

第二十二篇:Prisma 完全指南 —— 数据翻译官事务所宇宙

第十一章:一个完整示例 —— 从章程到翻译官到事务办理

1 分钟 165 字 第 410 / 962 个阅读单元

下面给一个完整、能把核心心智串起来的示例。

#1. schema.prisma
prisma
datasource db {
  provider = "postgresql"
}

generator client {
  provider = "prisma-client"
  output   = "../src/generated/prisma"
}

model User {
  id        Int      @id @default(autoincrement())
  email     String   @unique
  name      String?
  posts     Post[]
  createdAt DateTime @default(now())
}

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())
}

model AuditLog {
  id        Int      @id @default(autoincrement())
  action    String
  entity    String
  entityId  Int
  createdAt DateTime @default(now())
}
#2. 生成 Client
bash
prisma generate
#3. 业务代码:blog-demo.ts
ts
import { PrismaClient } from "./generated/prisma";

const prisma = new PrismaClient();

async function main() {
  // 1) 如果作者存在就更新,不存在就创建
  const author = await prisma.user.upsert({
    where: { email: "lin@city.dev" },
    update: { name: "Lin" },
    create: {
      email: "lin@city.dev",
      name: "Lin",
      posts: {
        create: [
          {
            title: "Prisma 不是魔法,是事务所",
            content: "先把 schema 想清楚,再写业务。",
          },
        ],
      },
    },
    include: { posts: true },
  });

  console.log("作者及其文章:", author);

  // 2) 查询已发布文章,并把作者信息一起带出来
  const publishedPosts = await prisma.post.findMany({
    where: { published: true },
    select: {
      id: true,
      title: true,
      createdAt: true,
      author: {
        select: {
          id: true,
          email: true,
          name: true,
        },
      },
    },
    orderBy: { createdAt: "desc" },
  });

  console.log("已发布文章:", publishedPosts);

  // 3) 用事务把“发布文章 + 写审计日志”打包
  const postId = author.posts[0]?.id;
  if (!postId) {
    throw new Error("没有可发布的文章");
  }

  const publishedPost = await prisma.$transaction(async (tx) => {
    const post = await tx.post.findUnique({
      where: { id: postId },
      select: { id: true, published: true },
    });

    if (!post) {
      throw new Error("文章不存在");
    }

    if (post.published) {
      return tx.post.findUnique({ where: { id: post.id } });
    }

    await tx.post.update({
      where: { id: post.id },
      data: { published: true },
    });

    await tx.auditLog.create({
      data: {
        action: "PUBLISH_POST",
        entity: "Post",
        entityId: post.id,
      },
    });

    return tx.post.findUnique({
      where: { id: post.id },
      include: {
        author: {
          select: { id: true, email: true, name: true },
        },
      },
    });
  });

  console.log("发布后的文章:", publishedPost);
}

main()
  .catch((error) => {
    console.error(error);
    process.exit(1);
  })
  .finally(async () => {
    await prisma.$disconnect();
  });

这个示例一口气串起了:

  • schema
  • model
  • relation
  • prisma generate
  • upsert
  • 关联读取
  • 事务
  • 审计记录

它最值得记住的不是代码本身,而是这条工作流:

text
先定义数据契约
   ▼
生成类型安全入口
   ▼
在业务代码里清晰表达查询与写入意图
   ▼
用事务保护关键业务边界