JavaScript >> Javascript 文檔 >  >> Node.js

將 PostgreSQL 與 Node.js 和 node-postgres 一起使用

簡介

在本文中,我們將討論如何將 PostgreSQL 與 Node.js 集成。

為了更好地閱讀本文,我們建議您有使用 Node.js 和 SQL 語句的經驗。我們將在本文中使用簡單的 javascript ES6 語法。

您可以使用幾種不同的客戶端將 PostgreSQL 與 Node.js 集成。在本文中,我們將使用 node-postgres 模塊。與其他 PostgreSQL 客戶端相比,它是一個流行且成熟的模塊。

此外,您也可以將 PostgreSQL 與 ORM 一起使用,例如 Sequelize。但我們不會在本文中使用這樣的 ORM 模塊。相反,我們將使用普通的 SQL 查詢,然後您可以根據這些查詢構建更複雜的數據庫交互。

PostgreSQL

PostgreSQL 是一種流行的 SQL 數據庫。在過去的 30 多年裡,它一直在積極發展,被認為是目前最先進的關係數據庫之一。與其他可用的關係數據庫相比,PostgreSQL 也易於學習和設置。由於其免費和開源的性質,這是初創公司的熱門選擇。

PostgreSQL 是一個跨平台數據庫,可在所有主要操作系統上運行。不過,操作系統之間的配置和訪問/數據庫創建略有不同。

在本文中,我們將使用 Ubuntu 18.04,這是一個流行的 Linux 平台,默認包含 PostgreSQL。如果您使用不同的操作系統,某些步驟可能會有所不同。

項目配置

讓我們開始使用默認設置的簡單空白 Node.js 項目:

$ npm init -y

然後,讓我們使用 npm 安裝 node-postgres 模塊,用於連接 Postgres 並與之交互:

$ npm install --save pg

實現 CRUD 操作

啟動我們的項目後,讓我們繼續配置數據庫。之後,我們將編寫一些基本的 CRUD 功能。

數據庫配置

與所有關係數據庫一樣,我們將從創建一個並連接到它開始。您可以使用 CLI 或基於 GUI 的客戶端來執行此操作。由於通過 CLI 進行設置非常簡單,因此我們將這樣做。

對於 Ubuntu,默認的 psql 命令將進入 CLI。 PostgreSQL 將創建一個名為 postgres 的用戶 在基於 Linux 的平台上訪問數據庫。因此,我們可以使用以下命令以 postgres 的身份登錄 用戶:

$ sudo -i -u postgres

然後運行進入CLI:

$ psql

你應該會看到一個類似這樣的命令 shell:

要查看當前存在的數據庫,讓我們使用 \list\l 命令:

現在,讓我們使用 SQL 查詢創建自己的:

CREATE DATABASE testdb;

通過運行這個命令,我們正在創建一個 testdb 數據庫並看到輸出,確認我們的命令:

CREATE DATABASE

由於創建了數據庫,我們現在可以訪問它。而 PostgreSQL 創建一個默認的 postgres 用戶,默認不設置密碼。如果您想設置密碼(而不是將其留空),請使用 \password 命令:

設置好密碼後,我們就可以在項目中使用數據庫了。

連接數據庫

您有兩個選項可以使用 node-postgres 連接到 PostgreSQL 服務器 模塊。一種選擇是使用單個客戶端。另一種方法是使用連接池。但是,如果您的應用程序非常頻繁地使用數據庫,那麼池將是比使用單個客戶端更好的選擇。

使用 node-postgres 連接到數據庫 模塊可以通過兩種方式完成 - 使用 單個客戶端 並使用連接池 .

我們將在本文後面介紹如何使用連接池連接到數據庫。目前,為了簡潔起見,讓我們使用單個客戶端連接到數據庫:

const { Client } = require('pg');

const client = new Client({
    user: 'postgres',
    host: 'localhost',
    database: 'testdb',
    password: '1234abcd',
    port: 5432,
});

client.connect();

在這裡,我們手動配置了選項。但是,您可以連接到數據庫而無需傳遞任何這些:

const { Client } = require('pg');

const client = new Client();
client.connect();

但話又說回來,節點 需要 了解如何 連接到數據庫,所以我們將通過環境變量提供它們:

PGUSER=dbuser
PGHOST=database.server.com
PGPASSWORD=secretpassword
PGDATABASE=mydb
PGPORT=3211

如果你沒有自己配置,模塊會使用默認值:

PGHOST='localhost'
PGUSER=process.env.USER
PGDATABASE=process.env.USER
PGPASSWORD=null
PGPORT=5432

在 Linux 上,process.env.USER 將保存當前登錄用戶的值。

創建表格

為數據插入準備好數據庫後,讓我們創建一些表來存儲我們的數據。與所有基於 SQL 的數據庫一樣,我們將使用 CREATE TABLE 查詢:

CREATE TABLE [table_name] (
    [column1] [datatype],
    [column2] [datatype],
    [column3] [datatype],
   ....
);

一個表由組成 ,並且每一列都有一個數據類型。例如,一個 firstName 列將有 varchar 作為數據類型,表示一個可變大小的String。

如果您想了解更多關於支持的數據類型,PostgreSQL 文檔很好地列出了它們。

話雖如此,我們可以使用這個查詢在數據庫中創建一個表:

const query = `
CREATE TABLE users (
    email varchar,
    firstName varchar,
    lastName varchar,
    age int
);
`;

要實際對數據庫運行此查詢,我們使用 query() client 中的函數 我們之前設置的對象:

client.query(query, (err, res) => {
    if (err) {
        console.error(err);
        return;
    }
    console.log('Table is successfully created');
    client.end();
});

注意: 別忘了end() 運行查詢後與客戶端的連接。

運行這段代碼將創建我們的表格並打印出來:

Table is successfully created

這也可以使用 Promise 和 async/await 來實現 .由於數據庫調用可能會失敗,因此使用 Promise 更有意義:

client
    .query(query)
    .then(res => {
        console.log('Table is successfully created');
    })
    .catch(err => {
        console.error(err);
    })
    .finally(() => {
        client.end();
    });

正如您在示例中看到的,我們可以使用 finally 塊來關閉與數據庫的連接。所以即使查詢拋出 err , 連接將被關閉。

或者,我們可以使用 async/await 語法也是:

try {
    const res = await client.query(query);
    console.log('Table is successfully created');
} catch (err) {
    console.log(err.stack);
} finally {
    client.close();
}

所有這些方法都應該產生相同的結果:

Table is successfully created

為了驗證這一點,讓我們使用 psql 檢查數據庫的命令行界面。打開終端,使用 psql 啟動 shell ,並使用 \c [database] 選擇數據庫 命令。 \c\connect 的簡寫 :

\c testdb

然後就可以列出數據庫testdb中的表了 通過運行 \dt 命令:

您還可以通過提供名稱來查詢特定表:

testdb=# \dt FOO

此查詢將顯示名為 FOO 的表 .

創建/插入數據

我們可以使用 SQL INSERT INTO 向表中插入數據的語句:

INSERT INTO [table_name] ([column1], [column2], [column3], ...)
VALUES ([value1], [value2], [value3], ...);

為了使這個查詢具體化,讓我們插入我們自己的值並構造一個查詢:

const query = `
INSERT INTO users (email, firstName, lastName, age)
VALUES ('[email protected]', 'john', 'doe', 21)
`;

最後,讓我們對數據庫運行查詢:

client.query(query, (err, res) => {
    if (err) {
        console.error(err);
        return;
    }
    console.log('Data insert successful');
    client.end();
});

注意: 和上次一樣,這個函數可以用async/await來寫 句法。為簡潔起見,省略了這些額外的示例。

免費電子書:Git Essentials

查看我們的 Git 學習實踐指南,其中包含最佳實踐、行業認可的標準以及隨附的備忘單。停止谷歌搜索 Git 命令並真正學習 它!

運行此代碼會將用戶插入我們的數據庫並打印出來:

Data insert successful

為了驗證這一點,在我們的 testdb 數據庫,運行 SELECT 聲明:

SELECT * from users;

我們可以明顯看到用戶確實創建成功了:

檢索/選擇數據

要從數據庫中檢索數據,SELECT 使用語句:

SELECT [column1], [column2], ...
FROM [table_name]
WHERE [condition];

您可以通過指定特定列來選擇它們,也可以使用 * 選擇表的所有字段 通配符。或者,您可以使用 WHERE 獲得更多條件的創意 聲明。

這裡我們從 users 中選擇所有行和所有列 數據庫:

const query = `
SELECT *
FROM users
`;

現在,要對數據庫運行此查詢,我們將使用 client 再次:

client.query(query, (err, res) => {
    if (err) {
        console.error(err);
        return;
    }
    for (let row of res.rows) {
        console.log(row);
    }
    client.end();
});

運行此代碼將產生:

{
email: '[email protected]',
firstname: 'john',
lastname: 'doe',
age: 21
}
{
email: '[email protected]',
firstname: 'anna',
lastname: 'dias',
age: 35
}

此查詢返回全部 用戶添加到數據庫。您還可以按字段過濾用戶。

例如,如果我們想返回所有 30 歲以下的用戶,我們會添加一個 WHERE 子句:

const query = `
SELECT *
FROM users
WHERE age<30
`;

然後,我們針對數據庫運行它:

client.query(query, (err, res) => {
    if (err) {
        console.error(err);
        return;
    }
    for (let row of res.rows) {
        console.log(row);
    }
    client.end();
});

運行此代碼將產生:

{
email: '[email protected]',
firstname: 'john',
lastname: 'doe',
age: 21
}

更新數據

要更新已經存在的數據,我們可以使用 UPDATE 聲明:

UPDATE [table_name]
SET [column1] = [value1], [column2] = [value2], ...
WHERE [condition];

您可以使用 SET 為每一列設置每個更新的值 關鍵詞。 WHERE 之後 子句,你可以定義哪些條目應該被更新的條件。

讓我們填充我們的查詢:

const query = `
UPDATE users
SET age = 22
WHERE email = '[email protected]'
`;

現在,讓我們對數據庫運行查詢:

client.query(query, (err, res) => {
    if (err) {
        console.error(err);
        return;
    }
    if (err) {
        console.error(err);
        return;
    }
    console.log('Data update successful');
    client.end();
});

運行這段代碼將更新滿足 WHERE 的條目 子句並打印出來:

Data update successful

為了驗證,讓我們檢查一下我們的數據庫:

刪除數據

最後,要刪除數據,我們可以使用 DELETE 聲明:

DELETE FROM [table_name]
WHERE [condition];

請謹慎使用此語句,因為您可能會意外刪除超出預期的內容。

讓我們填充我們的查詢:

const query = `
DELETE FROM users
WHERE email = '[email protected]'
`;

最後,針對數據庫運行它:

client.query(query, (err, res) => {
    if (err) {
        console.error(err);
        return;
    }
    if (err) {
        console.error(err);
        return;
    }
    console.log('Data delete successful');
    client.end();
});

運行此代碼將刪除滿足 WHERE 的條目 子句並打印出來:

Data delete successful

為了驗證,我們看一下數據庫:

池化

如果您的應用程序經常使用數據庫,那麼當您有許多用戶請求時,使用單個客戶端連接到數據庫可能會降低應用程序的速度。解決這個問題最簡單、最方便的方法是使用連接池。

通常,當新客戶端連接到數據庫時,建立連接和驗證的過程大約需要 20-30 毫秒。當您運行更多查詢導致延遲數秒時,這一點非常重要,這最終可能會導致最終用戶體驗不滿意。

此外,PostgreSQL 服務器在給定時間只能處理有限數量的客戶端,這取決於您的服務器內存。因此,如果在一秒鐘內進行 100 個查詢 - 此限制可能會使您的服務器崩潰。

此外,對於單個連接,客戶端一次只能處理一個請求,這進一步減慢了速度。

在這種情況下,您可以使用 pg-pool 模塊來解決這個問題。

創建池

首先導入Pool pg 中的類 模塊:

const { Pool } = require('pg');

然後,讓我們創建一個新的池對象:

const pool = new Pool({
    user: 'postgres',
    host: 'localhost',
    database: 'testdb',
    password: '1234abcd',
    port: 5432,
});

如果您不配置用戶名、主機和其他屬性,則必須在配置文件中為這些屬性定義環境變量。和配置單個客戶端的時候差不多。

接下來,讓我們為池定義一個錯誤處理程序。如果從池中拋出任何錯誤,將觸發此事件中的回調:

pool.on('error', (err, client) => {
    console.error('Error:', err);
});

這涵蓋了我們在網絡錯誤的情況下。

然後,使用 pool 對象,我們連接到數據庫並使用 client 在該池中執行查詢:

const query = `
SELECT *
FROM users
`;

pool.connect((err, client, done) => {
    if (err) throw err;
    client.query(query, (err, res) => {
        done();
        if (err) {
            console.log(err.stack);
        } else {
            for (let row of res.rows) {
                console.log(row);
            }
        }
    });
});

這應該產生:

{
  email: '[email protected]',
  firstname: 'john',
  lastname: 'doe',
  age: 21
}
{
  email: '[email protected]',
  firstname: 'anna',
  lastname: 'dias',
  age: 35
}

同樣,在這種情況下使用 Promise 更有意義:

pool.connect()
    .then((client) => {
        client.query(query)
            .then(res => {
                for (let row of res.rows) {
                    console.log(row);
                }
            })
            .catch(err => {
                console.error(err);
            });
    })
    .catch(err => {
        console.error(err);
    });

甚至是 async/await 語法:

(async () => {
    try {
        const client = await pool.connect();
        const res = await client.query(query);

        for (let row of res.rows) {
            console.log(row);
        }
    } catch (err) {
        console.error(err);
    }
})();

使用光標讀取大型查詢

通常,從查詢中接收到的數據會直接加載到內存中。數據集越大,內存佔用越高。

因此,當您嘗試查詢可能包含數千條記錄的大型數據集時 - 將其全部加載到內存中效率非常低,而且通常是不可能的。在這種情況下,游標可以通過一次檢索有限數量的記錄來幫助您。

從某種意義上說,使用游標類似於流數據,因為您將在較小的塊中按順序訪問它。為了使用光標,我們必須安裝 pg-cursor 模塊優先:

$ npm install --save pg pg-cursor

我們將傳遞一個 new Cursorquery() 功能。 cursor 在我們使用 read() 指定限制之前,實際上不會檢索任何信息 方法:

const { Pool } = require('pg');
const Cursor = require('pg-cursor');

const pool = new Pool({
    user: 'postgres',
    host: 'localhost',
    database: 'testdb',
    password: '1234abcd',
    port: 5432,
});

(async () => {
    const client = await pool.connect();
    const query = 'SELECT * FROM users';

    const cursor = await client.query(new Cursor(query));

    cursor.read(1, (err, rows) => {
        console.log('We got the first row set');
        console.log(rows);

        cursor.read(1, (err, rows) => {
            console.log('This is the next row set');
            console.log(rows);
        });
    });
})();

cursorread() 方法讓我們定義要從當前 cursor 中檢索多少行 實例。在此示例中,為簡單起見,我們限制了一條記錄的行數。之後我們又讀取了另一組行。

如果您已到達數據庫中行的末尾,rows 數組的長度為 0 .

結論

PostgreSQL 是一個非常流行的免費開源關係數據庫。 node-postgres 模塊是一個廣泛使用且成熟的模塊,它將 Node.js 連接到 PostgreSQL。

在本文中,我們設置了一個 PostgreSQL 數據庫並通過一個簡單的 Node.js 腳本開發了基本的 CRUD 功能。然後,我們探索了池化支持和游標的使用來限制檢索到的數據。

與往常一樣,源代碼在 GitHub 上可用。


Tutorial JavaScript 教程
  1. 移動鼠標:mouseover/out, mouseenter/leave

  2. React 簡介

  3. 在開源縮略圖庫 ReactJS 中生成視頻縮略圖。

  4. 如何在不使用 npm/yarn 包的情況下在 React App 中使用 FontAwesome 圖標?

  5. 靜態站點生成器發生了什麼?為什麼人數在增加?

  6. React 最佳實踐 - 2022 年編寫更好代碼的方法

  7. Leetcoder 談論他的求職 - 編碼面試模仿

  1. React pdf-renderer 不顯示字符 č、ć 和 đ

  2. Express JS 和 Nunjucks 教程:第 1 部分 Express JS 基礎

  3. Nodejs、Express 和 Glitch 應用程序

  4. 聊天框,自動滾動到底部

  5. ReactJs 的甘特圖可編輯圖表

  6. 將您的 dev.to 帖子添加到您的網站

  7. React v18 的新功能

  1. React 教程 – 如何構建文本翻譯 PWA

  2. 使用 StencilJS 和 Ionic 4 構建聊天 Web 組件

  3. TypeScripts Clothing 中的 JSDoc

  4. Javascript中的異步編程