Prisma一命速通
最近在学习nestjs,当我准备捣鼓一个后台服务的时候我想起了还没有搞数据库,所以准备像以前那样写mysql connect 然后手动拼接sql语句。不得不说都6202年了我这还像个原始人一样手动操作sql语句,而且还有sql注入的风险。所以我上网查了查发现现在都早就不用手动拼接sql了,直接用封装好的api处理请求sql。
像与java类似的TypeORM,以及现在非常流行的Prisma。他们都不需要你手动拼接sql语句,而且根据定义的类型自动生成表结构。
所以话不多说直接开搞吧,我用的是mysql + nestjs,直接快进到极速入门
拉一个nest cli 然后猛猛安装官网文档的prisma
pnpm add prisma @prisma/client @prisma/adapter-mariadb dotenv
Prisma 用于运行prisma init/migrate/generate等命令;
@prisma/client 用于查询数据库
@prisma/adapter-mariadb 用于将client连接到mysql的驱动适配器
dotenv 用于加载环境变量
初始化prisma ORM
在刚拉的nestjs根目录执行npx prisma
然后使用npx prisma init --datasource-provider mysql --output ../generated/prisma
创建prisma文件夹和schema.prisma文件,生成根目录的prisma.config.ts文件和生成.env环境变量文件;
在.env环境变量文件里新增
DATABASE_URL="mysql://username:password@localhost:3306/mydb"
DATABASE_USER="username"
DATABASE_PASSWORD="password"
DATABASE_NAME="mydb"
DATABASE_HOST="localhost"
DATABASE_PORT=3306
定义表结构
打开prisma/schema.prisma文件(可以自己安装vscode插件使其阅读体验 up)
这个文件里model [表名] {
// 表结构描述 后面会根据此自动生成表结构
}
例如:
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
posts Post[]
}
model Post {
id Int @id @default(autoincrement())
title String
content String? @db.Text
published Boolean @default(false)
author User @relation(fields: [authorId], references: [id])
authorId Int
}
创建数据库表
执行npx prisma migrate dev --name init
执行这条命令会自动在配置的数据库里创建table
创建Prisma Client
执行npx prisma generate
这将会在根目录生成generated/prisma文件夹,里面的client待会儿会用到它来查询数据库
Module
创建一个全局的数据库module用于复用prisma
import { Global, Module } from '@nestjs/common';
import { PrismaClient } from '../../generated/prisma/client';
import { PrismaMariaDb } from '@prisma/adapter-mariadb';
const adapter = new PrismaMariaDb({
host: process.env.DATABASE_HOST,
user: process.env.DATABASE_USER,
password: process.env.DATABASE_PASSWORD,
database: process.env.DATABASE_NAME,
connectionLimit: 5,
});
const prisma = new PrismaClient({ adapter });
@Global()
@Module({
providers: [
{
provide: 'PRISMA',
useValue: prisma,
},
],
exports: ['PRISMA'],
})
export class PrismaModule {}
使用示例
由于我export了Prisma并全局了,所以直接在service里这么用就可以了。
import { Injectable, Inject } from '@nestjs/common';
import { PrismaClient } from '@prisma/client';
@Injectable()
export class UserService {
constructor(@Inject('PRISMA') private prisma: PrismaClient) {}
async createUser(email: string, password: string) {
return this.prisma.user.create({
data: { email, password },
});
}
async findUserByEmail(email: string) {
return this.prisma.user.findUnique({
where: { email },
});
}
}
踩坑及补充
this.prisma.user这里的user是根据你写的prisma/schema.prisma model User这个表自动推断的。所有写在这里的model类型都能被typescript自动推断!而且也只能写在这里,这里是定义数据库表结构的地方!
我没有连本地sql,而是连接远程服务器sql并开启本地代理(不推荐开启远程3306端口哈,不然被爆破就炸了)。
ssh -L 3306:127.0.0.1:3306 -N -f 用户名@服务器IP
参数解释:
-L 3306:127.0.0.1:3306:将本地的3306端口流量转发到远程服务器的127.0.0.1:3306。-N:只建立隧道,不执行远程命令(不进入终端)。-f:后台运行。
远程sql基于安全等的考虑,不允许mysql root用户走本地代理,所以需要在mysql里创建一个其他用户,所以如果遇到这样的报错“Access denied for user 'root'@'localhost'” 就需要建一个别的账户用户远程ssh隧道连接了。
注意:我这里本地并没有安装mysql,所以我本地的3306并没有被占用,如果本地安装了mysql并在运行的话3306应该已经被占用了,此时不要用3306端口代理,记得把.env里的端口也一并更改。
登陆远程sql后,如果还没有建库的话先创建库
CREATE DATABASE blog_db
DEFAULT CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;
创建账户
CREATE USER 'blog_user'@'localhost'
IDENTIFIED BY '强密码';
授权
GRANT ALL PRIVILEGES ON blog_db.* TO 'blog_user'@'localhost';
FLUSH PRIVILEGES;
更改.env环境变量里的DATABASE_URL
DATABASE_URL="mysql://blog_user:强密码@localhost:3306/blog_db"
此时再执行npx prisma migrate dev --name init大概就能正常了。
如果遇到的是P1010 这是权限拒绝的意思,需要给用户授权,即上面的授权操作。
而P1001是连不上的意思,需要自己查看帐号及密码是否正确。
如果错误是P3014,这是Prisma想要建立一个影子数据库,所以我们需要给它建这个库
CREATE DATABASE prisma_shadow
DEFAULT CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;
别忘了把这个影子数据库授权给用户!
建好数据库并给用户授权后在本地根目录的prisma.config.ts里配置影子数据库的地址
import 'dotenv/config';
import { defineConfig, env } from 'prisma/config';
export default defineConfig({
schema: 'prisma/schema.prisma',
migrations: {
path: 'prisma/migrations',
},
datasource: {
url: env('DATABASE_URL'),
shadowDatabaseUrl: env('SHADOW_DATABASE_URL'),
},
});
别忘了在.env环境里设置SHADOW_DATABASE_URL
.env差不多是这样
DATABASE_URL="mysql://[mysql账户]:[密码]@localhost:3306/[数据库名称]"
SHADOW_DATABASE_URL="mysql://[mysql账户]:[密码]@localhost:3306/[影子数据库]"
DATABASE_USER=[mysql账户]
DATABASE_PASSWORD=[密码]
DATABASE_NAME=[数据库名称]
DATABASE_HOST="localhost"
DATABASE_PORT=3306
前面我建立了ssh隧道来访问远程sql,通过这个命令来查看本地是否建立了连接
ps aux | grep "[s]sh -L 3306"
如果terminal里返回了类似
ps aux | grep "[s]sh -L 3306" xxxxxx 21093 0.0 0.0 435300912 2416 ?? Ss 2:11下午 0:00.01 ssh -L 3306:127.0.0.1:3306 -N -f root@[远程服务器地址]
则说明已经成功建立隧道了,如果想要停止,可以执行kill 21093或者是pkill -f "ssh -L 3306"
更新表
前面初始化的时候执行了init来初始化表结构,后续如果新增了表,不需要再次init,而是npx prisma migrate dev --name add_[新的表名],如果出错了可以先格式化一下npx prisma format
同时为了及时更新ts类型推导,你还要手动更新prisma/client避免新增的表无法自动推导,执行npx prisma generate即可