ORM(Object relational mappers) 的含义是,将数据模型与 Object 建立强力的映射关系,这样我们对数据的增删改查可以转换为操作 Object(对象)。

Prisma 是一个现代 Nodejs ORM 库,根据 Prisma 官方文档 可以了解这个库是如何设计与使用的。

概述

Prisma 提供了大量工具,包括 Prisma Schema、Prisma Client、Prisma Migrate、Prisma CLI、Prisma Studio 等,其中最核心的两个是 Prisma Schema 与 Prisma Client,分别是描述应用数据模型与 Node 操作 API。

与一般 ORM 完全由 Class 描述数据模型不同,Primsa 采用了一个全新语法 Primsa Schema 描述数据模型,再执行 prisma generate 产生一个配置文件存储在 node_modules/.prisma/client 中,Node 代码里就可以使用 Prisma Client 对数据增删改查了。

Prisma Schema

Primsa Schema 是在最大程度贴近数据库结构描述的基础上,对关联关系进行了进一步抽象,并且背后维护了与数据模型的对应关系,下图很好的说明了这一点:

<img width=400 src="https://z3.ax1x.com/2021/10/17/5YwZoF.png">

可以看到,几乎与数据库的定义一模一样,唯一多出来的 posts 与 author 其实是弥补了数据库表关联外键中不直观的部分,将这些外键转化为实体对象,让操作时感受不到外键或者多表的存在,在具体操作时再转化为 join 操作。下面是对应的 Prisma Schema:

datasource db {provider = "postgresql"url      = env("DATABASE_URL")
}generator client {provider = "prisma-client-js"
}model Post {id        Int     @id @default(autoincrement())title     Stringcontent   String? @map("post_content")published Boolean @default(false)author    User?   @relation(fields: [authorId], references: [id])authorId  Int?
}model User {id    Int     @id @default(autoincrement())email String  @uniquename  String?posts Post[]
}

datasource db 申明了链接数据库信息;generator client 申明了使用 Prisma Client 进行客户端操作,也就是说 Prisma Client 其实是可以替换实现的;model 是最核心的模型定义。

在模型定义中,可以通过 @map 修改字段名映射、@@map 修改表名映射,默认情况下,字段名与 key 名相同:

model Comment {title @map("comment_title")@@map("comments")
}

字段由下面四种描述组成:

  • 字段名。
  • 字段类型。
  • 可选的类型修饰。
  • 可选的属性描述。
model Tag {name String? @id
}

在这个描述里,包含字段名 name、字段类型 String、类型修饰 ?、属性描述 @id

字段类型

字段类型可以是 model,比如关联类型字段场景:

model Post {id       Int       @id @default(autoincrement())// Other fieldscomments Comment[] // A post can have many comments
}model Comment {id     Int// Other fieldsPost   Post? @relation(fields: [postId], references: [id]) // A comment can have one postpostId Int?
}

关联场景有 1v1, nv1, 1vn, nvn 四种情况,字段类型可以为定义的 model 名称,并使用属性描述 @relation 定义关联关系,比如上面的例子,描述了 Commenct 与 Post 存在 nv1 关系,并且 Comment.postId 与 Post.id 关联。

字段类型还可以是底层数据类型,通过 @db. 描述,比如:

model Post {id @db.TinyInt(1)
}

对于 Prisma 不支持的类型,还可以使用 Unsupported 修饰:

model Post {someField Unsupported("polygon")?
}

这种类型的字段无法通过 ORM API 查询,但可以通过 queryRaw 方式查询。queryRaw 是一种 ORM 对原始 SQL 模式的支持,在 Prisma Client 会提到。

类型修饰

类型修饰有 ? [] 两种语法,比如:

model User {name  String?posts Post[]
}

分别表示可选与数组。

属性描述

属性描述有如下几种语法:

model User {id        Int     @id @default(autoincrement())isAdmin   Boolean @default(false)email     String  @unique@@unique([firstName, lastName])
}

@id 对应数据库的 PRIMARY KEY。

@default 设置字段默认值,可以联合函数使用,比如 @default(autoincrement()),可用函数包括 autoincrement()dbgenerated()cuid()uuid()now(),还可以通过 dbgenerated 直接调用数据库底层的函数,比如 dbgenerated("gen_random_uuid()")

@unique 设置字段值唯一。

@relation 设置关联,上面已经提到过了。

@map 设置映射,上面也提到过了。

@updatedAt 修饰字段用来存储上次更新时间,一般是数据库自带的能力。

@ignore 对 Prisma 标记无效的字段。

所有属性描述都可以组合使用,并且还存在需对 model 级别的描述,一般用两个 @ 描述,包括 @@id@@unique@@index@@map@@ignore

ManyToMany

Prisma 在多对多关联关系的描述上也下了功夫,支持隐式关联描述:

model Post {id         Int        @id @default(autoincrement())categories Category[]
}model Category {id    Int    @id @default(autoincrement())posts Post[]
}

看上去很自然,但其实背后隐藏了不少实现。数据库多对多关系一般通过第三张表实现,第三张表会存储两张表之间外键对应关系,所以如果要显式定义其实是这样的:

model Post {id         Int                 @id @default(autoincrement())categories CategoriesOnPosts[]
}model Category {id    Int                 @id @default(autoincrement())posts CategoriesOnPosts[]
}model CategoriesOnPosts {post       Post     @relation(fields: [postId], references: [id])postId     Int // relation scalar field (used in the `@relation` attribute above)category   Category @relation(fields: [categoryId], references: [id])categoryId Int // relation scalar field (used in the `@relation` attribute above)assignedAt DateTime @default(now())assignedBy String@@id([postId, categoryId])
}

背后生成如下 SQL:

CREATE TABLE "Category" (id SERIAL PRIMARY KEY
);
CREATE TABLE "Post" (id SERIAL PRIMARY KEY
);
-- Relation table + indexes -------------------------------------------------------
CREATE TABLE "CategoryToPost" ("categoryId" integer NOT NULL,"postId" integer NOT NULL,"assignedBy" text NOT NULL"assignedAt" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,FOREIGN KEY ("categoryId")  REFERENCES "Category"(id),FOREIGN KEY ("postId") REFERENCES "Post"(id)
);
CREATE UNIQUE INDEX "CategoryToPost_category_post_unique" ON "CategoryToPost"("categoryId" int4_ops,"postId" int4_ops);

Prisma Client

描述好 Prisma Model 后,执行 prisma generate,再利用 npm install @prisma/client 安装好 Node 包后,就可以在代码里操作 ORM 了:

import { PrismaClient } from '@prisma/client'const prisma = new PrismaClient()

CRUD

使用 create 创建一条记录:

const user = await prisma.user.create({data: {email: 'elsa@prisma.io',name: 'Elsa Prisma',},
})

使用 createMany 创建多条记录:

const createMany = await prisma.user.createMany({data: [{ name: 'Bob', email: 'bob@prisma.io' },{ name: 'Bobo', email: 'bob@prisma.io' }, // Duplicate unique key!{ name: 'Yewande', email: 'yewande@prisma.io' },{ name: 'Angelique', email: 'angelique@prisma.io' },],skipDuplicates: true, // Skip 'Bobo'
})

使用 findUnique 查找单条记录:

const user = await prisma.user.findUnique({where: {email: 'elsa@prisma.io',},
})

对于联合索引的情况:

model TimePeriod {year    Intquarter Inttotal   Decimal@@id([year, quarter])
}

需要再嵌套一层由 _ 拼接的 key:

const timePeriod = await prisma.timePeriod.findUnique({where: {year_quarter: {quarter: 4,year: 2020,},},
})

使用 findMany 查询多条记录:

const users = await prisma.user.findMany()

可以使用 SQL 中各种条件语句,语法如下:

const users = await prisma.user.findMany({where: {role: 'ADMIN',},include: {posts: true,},
})

使用 update 更新记录:

const updateUser = await prisma.user.update({where: {email: 'viola@prisma.io',},data: {name: 'Viola the Magnificent',},
})

使用 updateMany 更新多条记录:

const updateUsers = await prisma.user.updateMany({where: {email: {contains: 'prisma.io',},},data: {role: 'ADMIN',},
})

使用 delete 删除记录:

const deleteUser = await prisma.user.delete({where: {email: 'bert@prisma.io',},
})

使用 deleteMany 删除多条记录:

const deleteUsers = await prisma.user.deleteMany({where: {email: {contains: 'prisma.io',},},
})

使用 include 表示关联查询是否生效,比如:

const getUser = await prisma.user.findUnique({where: {id: 19,},include: {posts: true,},
})

这样就会在查询 user 表时,顺带查询所有关联的 post 表。关联查询也支持嵌套:

const user = await prisma.user.findMany({include: {posts: {include: {categories: true,},},},
})

筛选条件支持 equalsnotinnotInltltegtgtecontainssearchmodestartsWithendsWithANDORNOT,一般用法如下:

const result = await prisma.user.findMany({where: {name: {equals: 'Eleanor',},},
})

这个语句代替 sql 的 where name="Eleanor",即通过对象嵌套的方式表达语义。

Prisma 也可以直接写原生 SQL:

const email = 'emelie@prisma.io'
const result = await prisma.$queryRaw(Prisma.sql`SELECT * FROM User WHERE email = ${email}`
)

中间件

Prisma 支持中间件的方式在执行过程中进行拓展,看下面的例子:

const prisma = new PrismaClient()// Middleware 1
prisma.$use(async (params, next) => {console.log(params.args.data.title)console.log('1')const result = await next(params)console.log('6')return result
})// Middleware 2
prisma.$use(async (params, next) => {console.log('2')const result = await next(params)console.log('5')return result
})// Middleware 3
prisma.$use(async (params, next) => {console.log('3')const result = await next(params)console.log('4')return result
})const create = await prisma.post.create({data: {title: 'Welcome to Prisma Day 2020',},
})const create2 = await prisma.post.create({data: {title: 'How to Prisma!',},
})

输出如下:

Welcome to Prisma Day 2020
1
2
3
4
5
6
How to Prisma!
1
2
3
4
5
6

可以看到,中间件执行顺序是洋葱模型,并且每个操作都会触发。我们可以利用中间件拓展业务逻辑或者进行操作时间的打点记录。

如果你想开发小程序或者了解更多小程序的内容,可以通过专业开发公司,来帮助你实现开发需求:厦门在乎科技-专注厦门小程序定制开发、APP开发、网站开发、H5小游戏开发

建议收藏|一文带你读懂 Prisma 的使用相关推荐

  1. 一文带你读懂HTTP协议的前世今生

    点击上方蓝字关注我们 HTTP,Hypertext Transfer Protocol,超文本协议,是在万维网上传输文件(如文本.图形图像.声音.视频和其他多媒体文件)的规则集.如果web用户打开他们 ...

  2. DNN、RNN、CNN.…..一文带你读懂这些绕晕人的名词

    DNN.RNN.CNN.-..一文带你读懂这些绕晕人的名词 https://mp.weixin.qq.com/s/-A9UVk0O0oDMavywRGIKyQ 「撞脸」一直都是娱乐圈一大笑梗. 要是买 ...

  3. 一文带您读懂FCC、CE、CCC认证的区别

    一文带您读懂FCC.CE.CCC认证的区别 参考资料:https://3g.k.sohu.com/t/n411629823 FCC认证,CE认证,CCC认证是产品认证中比较常见的几个认证,前两者经常有 ...

  4. 机器学习中为什么需要梯度下降_机器学习101:一文带你读懂梯度下降

    原标题 | Machine Learning 101: An Intuitive Introduction to Gradient Descent 作者 | Thalles Silva 译者 | 汪鹏 ...

  5. 用程序员计算机算进制,一文带你读懂计算机进制

    hi,大家好,我是开发者FTD.在我们的学习和工作中少不了与进制打交道,从出生开始上学,最早接触的就是十进制,当大家学习和使用计算机时候,我们又接触到了二进制.八进制以及十六进制.那么大家对进制的认识 ...

  6. 一文带你读懂“经典TRIZ”

    本文承接上文<一文带第读懂TRIZ>,下面开始看第二个问题:什么是"经典TRIZ"? 很多书里都有对TRIZ的产生与发展的描述. 我个人在看了很多的书和文献以后,认为: ...

  7. 简单一文带你读懂Java变量的作用和三要素

    Java变量的作用 不只是java,在其他的编程语言中变量的作用只有一个:存储值(数据) 在java中,变量本质上是一块内存区域,数据存储在java虚拟机(JVM)内存中 变量的三要素 变量的三要素分 ...

  8. 一文带你读懂感知机的前世今生(上)

    一文带你读懂感知机的前世今生 前言 男女不分 什么是神经元 M-P神经元 全或无定律 McCulloch和Pitts 一种高度简化的模型 MP神经元和真值表 MP神经元的几何理解 后记 参考 前言 男 ...

  9. 《一文带你读懂:云原生时代业务监控》

    点击上方蓝字关注我们! 对业务来说,完备的应用健康性和数据指标的监控非常重要,通过采集准确的监控指标.配置合理的告警机制,我们能够提前或者尽早发现问题,并做出响应.解决问题,进而保证产品的稳定性,提升 ...

  10. au加载默认的输入和输出设备失败_一文带你读懂 C/C++ 语言输入输出流与缓存区...

    (给CPP开发者加星标,提升C/C++技能) 作者:技术让梦想更伟大 / 李肖遥 (本文来自作者投稿) 前言 有没有发现,基本上所有的C语言入门书籍,或者是我们的教程里面,第一个C语言程序实体,都是& ...

最新文章

  1. 使用现代C++如何避免bugs(下)
  2. #每天一种设计模式# 观察者模式
  3. 【运筹学】线性规划数学模型 ( 知识点回顾 | 可行解 | 最优解 | 阶梯型矩阵 | 阶梯型矩阵向量 | 基 | 基向量 | 基变量 | 非基变量 )
  4. matplotlib可视化必知必会富文本绘制方法
  5. 双时隙的工作原理_智能天线工作原理是什么 智能天线技术发展介绍【图文】...
  6. python七段数码管绘制单个数字_#Python语言程序设计Demo - 七段数码管绘制
  7. mysql用编号查询密码,【mysql中退出当前数据库】
  8. android app攻击与防范论文,基于Android平台的应用程序安全保护研究与应用
  9. VMware ubuntu16安装
  10. Vue自定义插件方法大全
  11. 【机器学习笔记】使用lightgbm画并保存Feature Importance
  12. IIS连接oralce数据提示“System.Data.OracleClient 需要 Oracle 客户端软件 8.1.7 或更高版本”...
  13. springboot办公OA考勤请假系统java
  14. 机器人动力学(雅克比)
  15. 二元二次不定方程(佩尔方程)
  16. git和Github
  17. error: #79: expected a type specifier
  18. 打包docker镜像,推送远程服务器,部署到k8s步骤
  19. Wireline SerDes,高速信号的均衡技术
  20. 《区块链助力粤港澳大湾区一体化发展报告(2022)》发布

热门文章

  1. 重学css 0x3 CSS效果
  2. Java 的 FileInputStream 是否需要 close
  3. 运行3项目显示Module ‘“vue“‘ has no exported member ‘xxxx‘. Did you mean ‘Xxxx‘? TS2305: Module ‘“…/…/node_
  4. ssm 项目中引用 百度bae的sdk 安装jar
  5. 【支持升级官方最新版】西部数码主机代理系统模板源码IDC网站源码虚拟主机代理管理系统
  6. 怎么使用关键词获取视频列表 API
  7. DataV-组件配置
  8. 为招商手机银行点个赞!
  9. 觉得为时已晚的时候,恰恰是最早的时候。
  10. 对于von Mises distribution(冯·米塞斯分布)的一点心得