Prisma操作Docker远程数据库PostgreSQL

前提条件:安装好Docker Desktop、NodeJS以及具备一定的TypeScript基础

本教程实验环境为Windows 11、NodeJS-20.15.0、Docker-27.2.0

本地部署的数据库同样可以实现,确保数据库可以成功连接即可

1. Docker部署PostgreSQL

1.1 首先创建一个空文件夹

D:\> mkdir postgresql
D:\> cd postgresql

1.2 配置Docker Compose

Docker Compose 简化了对整个应用程序堆栈的控制,使得在一个易于理解的 YAML 配置文件中轻松管理服务、网络和数据卷。 接下来我们创建配置文件来对它进行配置。

在上面创建好的空文件夹中创建配置文件docker-compose.yml,并编辑:

services:
postgres_db: # 服务名称
image: postgres:15.7 # 指定镜像及其版本
container_name: docker_postgres # 指定容器的名称
environment:
POSTGRES_PASSWORD: postgres
POSTGRES_DB: postgres
POSTGRES_USER: postgres
ports: # 端口映射
- "5432:5432"
volumes: # 数据持久化的配置
- data:/var/lib/postgresql/data
- log:/var/log/postgresql

volumes: # 数据卷
data:
log:

对配置文件中属性的一些注解:

image : 指定了要使用的 Docker 镜像及其版本。在这里,我们使用了官方的 PostgreSQL 15.7 版本镜像。为了确保系统的稳定性和兼容性,推荐使用 PostgreSQL 官方镜像的一个稳定版本而不是最新版latest。如果没有写版本,将会默认使用最新版本latest

contain_name: 容器名称。

environment: 环境变量,可以是数据库的用户名、数据库类型和密码。

ports: 端口。

volumes: 数据持久化卷。这个可以保证容器被删除的情况下,数据卷中的数据不会丢失。如果不需要可以把日志数据卷去掉。很多不被使用的数据卷可能会占用很大的存储空间。

1.3 部署和验证

接下来启动Docker Compose,它会按照配置文件来配置容器,我们已经配置了数据库镜像,所以启动后它会自动拉取Postgresql数据库镜像并部署运行:

D:\postgresql> docker compose up -d

如果没有刚才创建的docker-compose.yml配置文件,将会报错:

D:\postgresql> docker compose up -d
no configuration file provided: not found

部署成功显示如下:

[+] Running 1/1
[+] Running 1/2tgresql_default Created 0.1s
[+] Running 1/2tgresql_default Created 0.1s
[+] Running 1/2tgresql_default Created 0.1s
[+] Running 1/2tgresql_default Created 0.1s
[+] Running 1/2tgresql_default Created 0.1s
[+] Running 2/2tgresql_default Created 0.1s
✔ Network postgresql_default Created 0.1s
- Container docker_postgres Starting 0.6s
✔ Container docker_postgres Started 0.7s

这里我已经运行过相同指令,所以输出可能会有所不同,初次运行可能的成功结果如下:

[+] Running 15/15
✔ postgres_db Pulled 61.9s
09f376ebb190 Pull complete 8.9s
119215dfb3e3 Pull complete 2.0s
94fccb772ad3 Pull complete 10.7s
0fc3acb16548 Pull complete 8.6s
✔ d7dba7d03fe8 Pull complete 13.1s
898ae395a1ca Pull complete 12.6s
088e651df7e9 Pull complete 12.4s
✔ ed155773e5e0 Pull complete 14.2s
52df7d12fb73 Pull complete 33.4s
✔ bab1ecc22dc9 Pull complete 15.2s
1655a257a5b5 Pull complete 16.0s
978f02dfc247 Pull complete 18.0s
✔ d715d7d9aee0 Pull complete 17.8s
✔ b2e9251b2f8d Pull complete 19.8s
[+] Running 3/3
✔ Volume "postgresql_log" Created 0.0s
✔ Volume "postgresql_data" Created 0.0s
✔ Container docker_postgres Started 0.9s

接着,验证一下数据库是否在运行:

D:\postgresql>docker compose ps
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
docker_postgres postgres:15.7 "docker-entrypoint.s…" postgres_db 4 minutes ago Up 4 minutes 0.0.0.0:5432->5432/tcp

可以看到,数据库已成功部署。

2. Prisma远程操作数据库

2.1 环境配置

初始化一个 TypeScript 项目,并将 Prisma CLI 作为开发依赖项添加到其中。

D:\postgresql>npm init -y
Wrote to D:\postgresql\package.json:
{
"name": "postgresql",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"description": ""
}
D:\postgresql>npm install prisma typescript ts-node @types/node --save-dev
added 26 packages in 9s

这将创建一个包含 TypeScript 应用程序的初始设置的 package.json。接着初始化TypeScirpt:

D:\postgresql> npmx tsc --init

我们可以尝试调用prisma

D:\postgresql> npx prisma

然后,初始化prisma,并生成环境配置文件.env:

D:\postgresql> npx prisma init

.env中存放数据库的远程链接等内容。PostgreSQL的远程链接地址一般如下:

DATABASE_URL="postgresql://username:password@localhost:5432/sqlname"

记得将username password 以及 sqlname替换成刚才创建docker中的配置文件中的信息docker-compose.yml.

例如我的.env文件内容为:(参考我的docker-compose.yml)

DATABASE_URL="postgresql://postgres:postgres@localhost:5432/postgres"

2.2 创建数据库表

prisma中数据库表用model表示,它提供了多种属性类型等,在schema.prisma中写入两张表,关于数据库表的属性的设计可以参考官网:

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

datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}

model Profile {
id Int @id @default(autoincrement())
bio String
user User @relation(fields: [userId], references: [id])
userId Int @unique
}

model User {
id Int @id @default(autoincrement())
name String
age Int
profile Profile?
}

在进行数据库映射:

D:\postgresql> npx prisma migrate dev --name init

这样操作之后,远程对数据库的更改就可以同步到我们的数据库镜像中。

2.3 增删改查 (CURD)

创建一个index.ts文件,用来通过prisma操作数据库:

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

const prisma = new PrismaClient();

async function main() {
const user = await prisma.user.findMany();
console.log(user);
}

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

根据我们之前创建的两张表UserProfile,我们上面的操作是对User进行了查询操作。[table].findMany()方法是对表格的查询,请注意,根据prisma规范,表格的名称在创建是首字母应该大写,但是对表格进行操作时,表格的名称应全部改成小写,所以在上面的代码中,prisma.user.findMany()中的表格名称user是小写的。

运行index.ts文件:

D:\postgresql> npx ts-node index.ts

如果正确执行,那么会打印出一个空数组,显而易见,由于表格新创建,所以里面没有数据内容。

下面我们来写入数据库,修改index.ts:

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

const prisma = new PrismaClient();

async function addUser(){
await prisma.user.create({
data:{
name:"hi",
age: 18,
profile: {
create: {
bio: "I am a software developer"
}
}
}
})
const profile = await prisma.profile.findMany()
console.log(profile)
}

addUser()
.then(async () => {
await prisma.$disconnect()
})
.catch(async (e) => {
console.error(e)
await prisma.$disconnect()
process.exit(1)
})

在上面的代码中,我们向user写入了一个名字叫hi、年龄为18的人,并通过绑定同时写入了profile表格的一项bio属性。

再次执行index.ts,注意,我将prisma.user.findmany()中的user改成了profile,所以我这次查询输出的是Profile表格中的内容,最后的输出为:

[
{ id: 1, bio: 'I am a software developer', userId: 1 },
{ id: 2, bio: 'I am a software developer', userId: 2 },
{ id: 3, bio: 'I am a software developer', userId: 3 },
{ id: 4, bio: 'I am a software developer', userId: 4 }
]

可以看到确实向表格中插入的四条数据。

最后我们尝试来更新表格内容,更新的方法为update(),语法与上述查询和插入一样,修改main函数:

async function main() {
const profile = await prisma.profile.update({
where: { id: 1 },
data: { bio: 'I am a super satr!!!' },
})
console.log(profile)
}

我对Profile表格使用了更新方法,在id属性为1的位置,将bio属性值修改成了”I am a super satr!!!”,再次运行index.ts,最后的输出为:

[
{ id: 1, bio: 'I am a super satr!!!', userId: 1 },
{ id: 2, bio: 'I am a software developer', userId: 2 },
{ id: 3, bio: 'I am a software developer', userId: 3 },
{ id: 4, bio: 'I am a software developer', userId: 4 }
]

可以看到,数据已经成功更新到数据库中。