为了账号安全,请及时绑定邮箱和手机立即绑定

使用Node.js与PostgreSQL:原始SQL vs ORM,哪种更适合你?

通过 ChatGPT,和 DALL·E 3 生成的

在这篇文章中,我们将使用一个简单的 Node.Js API 示例来比较直接执行原始 SQL 与使用对象关系映射器来与 PostgreSQL 数据库进行交互的方法。

具体地讲,比如对于 Raw SQL,我们用node-postgres,比如对于 ORM,我们用[Prisma]。

你可以在这里获取演示代码:这里

咱们开始吧,行!

开始设置:

在我们开始比较这两种方法之前,先设置好数据库和Node.js相关的项目。

在数据库中建表

postgres 数据库中 public 模式里一个简单的 users 表。

    CREATE TABLE postgres.public."users" (  
        id uuid DEFAULT gen_random_uuid() PRIMARY KEY,  
        name VARCHAR(80) NOT NULL  
    )

/ 此 SQL 代码片段直接使用,无需翻译,因为它是一种不需要翻译的技术语法。/

搭建 Node.js 项目

yarn启动我们的项目,再装上express用来做路由

创建一个名为 `javascript+sql` 的文件夹 `mkdir javascript+sql`
切换到 `javascript+sql` 目录下 `cd javascript+sql`
使用 Yarn 初始化一个新的 Node.js 项目 `yarn init`
安装 Express 框架到项目中 `yarn add express`
创建一个名为 `index.js` 的新文件 `touch index.js`
index.js

这将是我们的项目起点,以下代码将被使用。

    import express from 'express';  
    var server = express();  

    server.listen(80);  

    server.get('/', async function (request, response) {  
        return response.json({  
            "message": "来自Itsuki的问候,你好!"  
        });  
    });

你可以运行 node index.js (或者如果你想在更改时自动重新编译,可以运行 nodemon index.js),然后在浏览器中访问 http://localhost:80 来进行快速测试。

直接使用 SQL (Node-postgres)

我们接下来要开始使用纯SQL了。

我将使用Node-postgres来进行演示用。这是我最喜欢的一个库,文档非常详尽,有很多示例。

安装

在命令行中输入以下命令来安装pg包:

使用npm安装 pg
连接到数据库:

我们将通过创建一个新的 [pg.Pool](https://node-postgres.com/apis/pool) 实例来连接数据库,如下所示。

    // 从'pg'模块导入数据库操作库
    import pg from 'pg';  
    // 解构赋值获取Pool对象
    const { Pool } = pg;  
    // 创建一个新的数据库连接池
    const pool = new Pool({  
        // 数据库主机地址从环境变量获取
        host: process.env.HOST,  
        // 数据库用户名从环境变量获取
        user: process.env.USER,  
        // 数据库密码从环境变量获取
        password: process.env.PASSWORD,  
        // 数据库名从环境变量获取
        database: process.env.DATABASE,  
        // 数据库端口号从环境变量获取
        port: process.env.PORT,  
        // 设置空闲超时时间为30000毫秒
        idleTimeoutMillis: 30000,  
    });

池最初为空,并在需要时按需创建新客户端。

我在这里列出了 config 对象的所有字段,但这些字段都是可选的。当池创建客户端实例时,该配置也会传递给池中每个客户端实例。

数据的使用和更改

我将用插入和查询为例。

不管我们在做什么操作,要运行直接的 SQL 查询,我们将会调用 [pool.query](https://node-postgres.com/features/queries),比如。client.query 支持同样的 API。实际上,pool.query 实际上是在内部直接调用 client.query

export class PgManager {  
    // 插入用户信息
    async 插入用户(username) {  
        const insertUser = `  
        插入到 public.users  
            (name)  
            值为($1)  
            返回所有列  
        `;  
        const result = await pool.query(insertUser, [username]);  
        // 输出结果到控制台
        console.log(result)  
        const userCreated = result.rows[0];  
        return userCreated  
    }  

    // 查询所有用户信息
    async 查询用户() {  
        const result = await pool.query("SELECT * FROM postgres.public.users");  
        // 输出结果到控制台
        console.log(result)  
        const users = result.rows;  
        return users  
    }  
}

要将参数传递给查询,我们可以通过拼接字符串,或者像上面那样使用带参数的查询。

我们只需要用几个端点来测试一下上面提到的它们。

    const pgManager = new PgManager();  
    server.post('/pg/users', async function (request, response) {  

        try {  
            if ("username" in request.query) {  
                const username = request.query.username;  
                const createdUser = await pgManager.insertUser(username);  
                return response.json({  
                    "user": createdUser  
                });  
            } else {  
                throw new Error('用户名未提供');  
            }  

        } catch (error) {  
            return response.json({  
                "error": `${error}`  
            });  
        }  
    });  

    server.get('/pg/users', async function (request, response) {  

        try {  
            const users = await pgManager.queryUsers();  
            return response.json({  
                "users": users  
            });  

        } catch (error) {  
            return response.json({  
                "error": `${error}`  
            });  
        }  
    });

这里以 GET localhost:80/pg/users 为例。

有好处

对我来说,这种方法最大的好处是不需要任何构建过程,也不需要抽象的概念。

当你在编写自动化测试时看到一个可以访问数据库的选项,这非常棒,因为你只需要写你的正常 SQL 并运行它!不需要设置!

我上面使用了 JavaScript,但你也可以使用 TypeScript 以获得一些类型安全性。例如,我们可以这样定义一个 interface User

用户接口 {  
  id: 用户ID;  
  name: 名称;  
} // 用户接口,定义用户ID和名称

并且用它来限制我们的回应

const users: User[] = await pgManager.queryUsers(); // 从pgManager查询用户列表并赋值给users
不足之处

接下来,让我们来看看这种方法存在的问题。

  • 没有单源实况(Single Source of Truth,SSOT
  • 没有内置的迁移处理方式
  • 没有验证SQL查询的正确性
  • 没有确保类型的准确性的手段
ORM (Prisma 代理)

Prisma(https://www.prisma.io/docs)是一个用于 Node.js 和 TypeScript 的 ORM,它具有直观的数据模型、自动化迁移和类型安全性。

是的,上面说的所有问题都已经考虑到了。

还有一些其他的知名 ORM,比如 TypeORM 和 Sequelize。你可以自己试试这些 ORM,找到你最喜欢的。在这里我们会用 Prisma。

安装命令行界面 (CLI)

你需要安装 Prisma CLI。

在命令行中输入以下命令来安装 prisma

npm install prisma --save-dev

开始设置(开始吧)

接下来,我们得运行一下来设置我们的项目

运行以下命令来初始化 Prisma:

npx prisma init

此命令将会如下

  • 创建一个名为 prisma 的新文件夹,其中包含一个名为 schema.prisma 的文件,该文件包含了您的数据库连接变量和 Prisma 模型定义。
  • 在项目根目录中创建 [.env](https://www.prisma.io/docs/orm/more/development-environment/environment-variables/env-files) 文件,用于定义数据库连接所需的环境变量。

记得去编辑 .env 文件里的 DATABASE_URL,改成你自己的数据库地址。它应该采用以下格式。

例如: postgresql://USER:PASSWORD@HOST:PORT/数据库?schema=模式

也确认你的prisma/schema.prisma包含如下内容。如果你在使用typescript,你的provider可能会有一些不同之处。

生成器 client {  
  提供器 = "prisma-client-js"  
}  

数据源 db {  
  提供器 = "postgresql"  
  url      = env("DATABASE_URL")  
}
模型数据信息

为了能够与我们的users表互动,我们首先需要为users表创建一个模型。

用户模型 users {  
  id        String     @db.Uuid @id @default(dbgenerated("gen_random_uuid()"))  // 默认生成随机UUID
  name      String  
}

如果你还没有设置好数据库表,你可以在这里运行命令 npx prisma migrate 来将数据模型映射到数据库模式中。具体来说,这个命令会执行以下操作:

  1. prisma/migrations 目录下为这次迁移创建一个新的 SQL 迁移文件。
  2. 将 SQL 迁移文件执行到数据库上。
  3. 后台运行了 prisma generate 命令(自动安装了 @prisma/client 包,并根据你的模型生成了定制的 Prisma 客户端 API)。

但我们已经有了数据库和表的搭建,我们就可以直接运行 npx prisma generate

安装:客户端

为了在项目中开始使用 Prisma 客户,我们需要安装 @prisma/client 包。

    npm install @prisma/client

在这里,我们使用npm安装@prisma/client包。

数据操作

我知道,配置真的超级多,但这样做有好处!

这里告诉你怎么插入和查询的操作。

    import { PrismaClient } from '@prisma/client'  // 引入PrismaClient模块

    const prisma = new PrismaClient()  // 创建PrismaClient实例

    // PrismaManager类用于管理和操作用户数据
    export class PrismaManager {  
        /**

* 插入用户方法,接受一个username参数,并创建一个新用户

* @param username 用户名

* @returns 返回创建的用户对象
         */
        async insertUser(username) {  
            const userCreated = await prisma.users.create({  
                data: {  
                    name: username  // 设置用户名
                },  
            })  
            console.log(userCreated)  // 输出创建的用户信息
            return userCreated  // 返回创建的用户对象
        }  

        /**

* 查询用户方法,返回所有用户列表

* @returns 返回一个包含所有用户对象的数组
         */
        async queryUsers() {  
            const users = await prisma.users.findMany()  // 查询所有用户
            return users  // 返回用户列表
        }  
    }

执行任何操作的基本格式是 {客户端}.{表名}.{操作}

简洁的代码逻辑,不含硬编码的SQL语句。

好处:

这个项目是你最准确的信息来源。

你有内建的方法来处理迁移问题。

类型和数据库模式保持一致。

我真的想说“简单的数据操作”已经不再足够了,如果你想进行更多的个性化查询,比如添加WHICHWHERE等这样的关键词就变得很重要。

当然,你也可以直接在 Prisma 中执行原始 SQL,那么为什么不用 pg 直接操作呢?

不足之处

太多了!

太多的设置显然会让大型或复杂数据库难以承受。

行了!

我不真的喜欢!

感谢阅读!希望这篇文章能帮助您在做决定时选择哪个更好!

我很想知道您对此的看法或您更喜欢的迁移工具使用Raw SQL库时!留下您的评论哦!

开心连上啦!

栈学 🎓

感谢你一直读到最后。在你离开之前,还有几句话要说:

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消