一、背景
最近在找适合nextjs的ORM框架,然后发现了prisma,github star数达到了42.7k。该框架号称下一代orm框架,包含客户端,迁移模块和ui管理后台。因为之前是做后端的,自然的对比了java的mybatis-plus,我英语半吊子,但实际感觉这个下一代的含义不是多么的现代和先进,而是多么的年轻。资料比较少,吐血整理了几点技巧,遇到类似场景希望能有帮助。
二、九点总结
1、如何分页
分页有两种方式,一种是使用官方的,一种用插件。官方只能设置skip和take,类似sql分页的limit和offset参数;插件的封装了查询总页数的逻辑,用起来更加方便。
const results = await prisma.post.findMany({
skip: 3,
take: 4,
});
或者使用cursor方式,这种性能好些
const secondQueryResults = await prisma.post.findMany({
take: 4,
skip: 1, // Skip the cursor
cursor: {
id: myCursor,
},
where: {
title: {
contains: 'Prisma' /* Optional filter */,
},
},
orderBy: {
id: 'asc',
},
})
const lastPostInResults = secondQueryResults[3] // Remember: zero-based index! :)
const myCursor = lastPostInResults.id // Example: 52
上面的例子中cursor就是每次查询比上次大的id。我感觉没必要,直接传入id查也一样的。
- 2)三方插件:
- github地址:github.com/sandrewTx08…
- 安装方式:
pnpm add prisma-paginate - 加入扩展
//引用
import { PrismaClient } from "@prisma/client";
import { extension } from "prisma-paginate";
//加入扩展
const prisma = new PrismaClient();
const xprisma = prisma.$extends(extension);
//分页查询
xprisma.model2
.paginate({ limit: 10, page: 1, select: { id: true } })
.then((result) => {
console.log(result);
});
上面page是页码,limit是每页页数,返回值有总页数,总数量和上一页下一页等。
2、 逻辑删除
一般公司要求不能直接物理删除数据,因此逻辑删除是必须滴,不然锅都不知道咋甩。逻辑删除也叫soft delete,该框架实现的比较繁琐,需要使用三方扩展,下面是一个推荐:github.com/olivierwilk… 使用很简单
-
安装:
npm install prisma-extension-soft-delete -
使用:经常使用delete作为标记删除字段,0表示未删除,1表是已删除
const extendedClient = client.$extends(
createSoftDeleteExtension({
models: { //哪些表/model使用
Comment: true,
Post: true,
},
defaultConfig: { //通用配置
field: "deleted",
createValue: (deleted) => {
if (deleted) return 1;
return 0;
},
},
})
);
这个框架会重写sql,查询自动添加delete条件。
3、自动填充
- 自动填充时间 model可以使用如下注解:@now或者@updatedAt。@updatedAt 是 Prisma 模型中的一个特殊字段属性,用于自动更新记录的最后修改时间。每当记录被更新时,Prisma 会自动为该字段设置当前时间,无需手动操作。
model user {
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
自动填充其他字段需要自定义middlware,以下是示例, 操作记录表填充userid
prisma.$use(async (params, next) => {
// Check incoming query type
if (params.model == 'operation_log' && params.action == 'create') {
params['user_id'] = getUserIdFromContext();
}
return next(params)
})
4、字段映射
数据库定义的字段是下划线,代码要求驼峰,则可以添加map映射,如下:
model MsgAccount {
id BigInt @id @default(autoincrement())
msgId Int? @map("msg_id")
tag String? @db.VarChar(50)
@@index([msgId], map: "idx_msg_id")
@@map("t_msg_account")
}
@map("msg_id") 将数据表的msg_id映射为msgId
@@map用于映射表名称。类似mybatisplus的@table注解
注意:prisma db pull同步操作会保留我们自定义的map注解
5、schema与db同步
为什么提这一点呢,个人开发的话各种迁移命令都可以,在公司有各种限制,不能随便改sql。因此常用的两个命令是
* npx prisma db pull: 同步sql到schema
* npx prisma migrate:修改model后重新生成客户端
每当数据表增加字段可以用 pull命令同步到model,如果model修改了,可以用migrate生成client,这样就不会影响到数据表
6、事务和回滚
事务几乎是业务中必须的操作,如转账等。prisma官方的事务操作如下
import { selectUserTitles, updateUserName } from '@prisma/client/sql'
const [userList, updateUser] = await prisma.$transaction([
prisma.$queryRawTyped(selectUserTitles()),
prisma.$queryRawTyped(updateUserName(2)),
])
或者放在一起使用
const updateTeam = prisma.team.update({
where: {
id: 1,
},
data: {
name: 'Aurora Adventures Ltd',
},
})
const updateUsers = prisma.user.updateMany({
where: {
teams: {
some: {
id: 1,
},
},
name: {
equals: null,
},
},
data: {
name: 'Unknown User',
},
})
await prisma.$transaction([updateUsers, updateTeam])
其他更复杂的操作可以见文档。感觉这块不如spring事务处理的更加精细,有各种传播机制和事务等,如果是复杂的大型业务还是建议使用java。
7、log日志
官方文档:www.prisma.io/docs/orm/pr…
const prisma = new PrismaClient({
log: [
{ emit: 'stdout', level: 'query' },
{ emit: 'stdout', level: 'error' },
{ emit: 'stdout', level: 'info' },
{ emit: 'stdout', level: 'warn' },
],
});
目前还没有封装特别细致的第三方库,可能后面会有。
8、自定义sql
有时候逻辑复杂,需要自定义sql,这个时候可以使用rawquery www.prisma.io/docs/orm/pr…
const email = "emelie@prisma.io";
const result = await prisma.$queryRaw(Prisma.sql`SELECT * FROM User WHERE email = ${email}`);
9、字段加密
像passowrd,身份证号,银行卡这些需要加密存储,可以这样定义mddleware
import { PrismaClient } from '@prisma/client'
import bcrypt from 'bcrypt'
const prisma = new PrismaClient().$extends({
query: {
user: {
$allOperations({ operation, args, query }) {
if (['create', 'update'].includes(operation) && args.data['password']) {
args.data['password'] = bcrypt.hashSync(args.data['password'], 10)
}
return query(args)
}
}
}
});
新建或者着update会自动更新密码
三、总结
使用下来的感受是能够是适配大部分的场景,类似中小企业或者个人开发,这个框架还支持sqlite这种嵌入式数据库,因此一般业务可以使用。从上面总结也能看出,这个框架不适合超大复杂业务,很多场景没有支持或者不够精细。
本人公众号大鱼七成饱,历史文章会在上面同步