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

將 H2 與 Node.js 和 Express 集成

簡介

H2 是一個用 Java 編寫的輕量級數據庫服務器。它可以嵌入到 Java 應用程序中,也可以作為獨立服務器運行。

在本教程中,我們將回顧為什麼 H2 可能是您項目的不錯選擇。我們還將學習如何集成 H2 使用 Node.js 構建一個簡單的 Express API。

H2 的特點

H2 構建時考慮到了性能。

雖然 H2 突出主要是因為它可以嵌入到 Java 應用程序中,它有一些有趣的特性也適用於它的服務器版本。接下來讓我們看看其中的一些。

尺寸和性能

.jar 用於服務器版本的文件約為 2MB。我們可以從 H2 下載它 網站,捆綁了額外的腳本和文檔。如果我們在 Maven Central 中搜索,我們可以下載 .jar 自己的文件。

H2 其嵌入式版本的性能大放異彩。即便如此,官方的基準測試表明它的客戶端-服務器版本也令人印象深刻。

內存數據庫和加密

內存數據庫不是持久的。所有數據都存儲在內存中,因此速度大大提高。

H2 網站解釋說,內存數據庫在原型設計或使用只讀數據庫時特別有用。

加密是保護靜態數據的另一個有用功能。數據庫可以使用 AES-128 加密 算法。

其他有用的功能

H2 還提供了集群模式,能夠運行多個服務器並將它們連接在一起。寫入同時在所有服務器中完成,而讀取則從集群中的第一台服務器完成。

H2 它的簡單性令人驚訝。它提供了幾個有用的功能,而且很容易設置。

讓我們開始一個 H2 服務器正在為以下部分做準備:

$ java -cp ./h2-1.4.200.jar org.h2.tools.Server -tcp -tcpAllowOthers -tcpPort 5234 -baseDir ./ -ifNotExists

tcp 開頭的參數 啟用與服務器的通信。 ifNotExists 參數允許在第一次訪問時創建數據庫。

API 說明和總圖

假設我們正在編寫一個 API 來註冊迄今為止發現的所有系外行星。系外行星是在我們太陽系之外發現的行星,圍繞其他恆星運行。

這是我們簡單的 API 定義,一個資源的 CRUD:

此定義以及我們接下來將看到的其餘代碼可在此 GitHub 存儲庫中找到。

這就是我們的應用程序在本教程結束時的樣子:

在圖表的左側,我們看到了 API 客戶端。該客戶端可以是 Swagger 編輯器的“試用”功能,也可以是任何其他客戶端,如 Postman 或 cURL。

在另一端,我們找到 H2 數據庫服務器,運行在 TCP 端口 5234 如上所述。

最後,我們中間的應用由兩個文件組成。第一個將有 Express 將回答所有 REST API 請求的應用程序。我們在上面定義中描述的所有端點都將添加到這個文件中。

第二個文件將具有持久性,訪問數據庫以執行 CRUD 操作的功能,使用 JDBC 包。

數據庫架構

將 Exoplanet 資源存儲到 H2 數據庫我們應該首先編寫基本的 CRUD 函數。讓我們從創建數據庫開始吧。

我們使用 JDBC 通過JDBC訪問數據庫的包:

var JDBC = require('jdbc');
var jinst = require('jdbc/lib/jinst');

if (!jinst.isJvmCreated()) {
  jinst.addOption("-Xrs");
  jinst.setupClasspath(['../h2-1.4.200.jar']);
}

var h2 = new JDBC({
  url: 'jdbc:h2:tcp://localhost:5234/exoplanets;database_to_lower=true',
  drivername: 'org.h2.Driver',
  properties: {
    user : 'SA',
    password: ''
  }
});

var h2Init = false;

function getH2(callback) {
  if (!h2Init)
    h2.initialize((err) => {
      h2Init = true;
      callback(err)
    });
  return callback(null);
};

function queryDB(sql, callback) {
  h2.reserve((err, connobj) => {
    connobj.conn.createStatement((err, statement) => {
      if(callback) {
        statement.executeQuery(sql, (err, result) => h2.release(connobj, (err) => callback(result)));
      } else {
        statement.executeUpdate(sql, (err) => h2.release(connobj, (err) => { if(err) console.log(err) }));
      }
    });
  });
};

module.exports = {
  initialize: function(callback) {
    getH2((err) => {
      queryDB("CREATE TABLE IF NOT EXISTS exoplanets ("
        + "  id INT PRIMARY KEY AUTO_INCREMENT,"
        + "  name VARCHAR NOT NULL,"
        + "  year_discovered SIGNED,"
        + "  light_years FLOAT,"
        + "  mass FLOAT,"
        + "  link VARCHAR)"
      );
    });
  },

initialize() 由於預先編寫了輔助函數,因此函數足夠簡單。如果系外行星表不存在,它會創建它。這個函數應該在我們的 API 開始接收請求之前執行。我們稍後會看到在哪裡使用 Express。

h2 使用連接字符串和憑據配置對像以訪問數據庫服務器。這個例子比較簡單,但在安全性方面還有改進的餘地。我們可以將憑據保存在其他地方,例如環境變量。

另外,我們需要將路徑添加到 H2 方法 jinst.setupClasspath() 上的 jar 文件 .這是因為 JDBC 包需要驅動連接到 H2 , org.h2.Driver .

JDBC 連接字符串以 /exoplanets;database_to_lower=true 結尾 .這意味著當第一次連接一個名為 exoplanets 的數據庫時 將被創建。此外,表名和列名將以小寫形式保存。這將簡化 API,因此不需要轉換屬性名稱。

queryDB() 函數使用 JDBC 訪問數據庫的庫方法。首先需要reserve() 與數據庫的連接。接下來的步驟是 createStatement() 然後是 executeQuery() 如果預期結果,或 executeUpdate() 否則。連接總是被釋放。

以上所有函數都可能返回錯誤。為了簡化這個例子,所有錯誤都沒有被檢查,但在實際項目中我們應該檢查它們。

getH2() 函數返回一個代表數據庫的對象。它將只創建一次該對象,使用與 Singleton 類始終只返回一個實例相同的機制。

現在讓我們驗證用戶數據並允許他們執行 CRUD 操作。

CRUD 數據庫函數

讓我們創建所需的功能,以允許此應用程序在系外行星上執行 CRUD 操作。我們將它們添加到 module.exports 這樣我們就可以輕鬆地從其他文件中引用它們並創建一個 persistence.js 我們可以使用的輔助模塊:

免費電子書:Git Essentials

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

module.exports = {
  getAll: function(callback) {
    getH2((err) => queryDB("SELECT * FROM exoplanets", (result) => {
      result.toObjArray((err, results) => callback(results))
    }));
  },
  get: function(id, callback) {
    getH2((err) => queryDB(`SELECT * FROM exoplanets WHERE id = ${id}`, (result) => {
      result.toObjArray((err, results) => { 
        return (results.length > 0) ? callback(results[0]) : callback(null);
      })
    }));
  },
  create: function(exoplanet) {
    getH2((err) => {
      columns = Object.keys(exoplanet).join();
      Object.keys(exoplanet).forEach((key) => exoplanet[key] = `'${exoplanet[key]}'`);
      values = Object.values(exoplanet).join();

      queryDB(`INSERT INTO exoplanets (${columns}) VALUES(${values})`);
    });
  },
  update: function(id, exoplanet) {
    getH2((err) => {
      keyValues = []
      Object.keys(exoplanet).forEach((key) => keyValues.push(`${key} = '${exoplanet[key]}'`));

      queryDB(`UPDATE exoplanets SET ${keyValues.join()} WHERE id = ${id}`);
    });
  },
  delete: function(id) {
    getH2((err) => queryDB(`DELETE FROM exoplanets WHERE id = ${id}`));
  },
};

get()getAll() 函數查詢數據庫以返回一個或多個系外行星。 API 會將它們直接返回給 API 客戶端。

所有函數主要是SQL查詢,但create()update() 值得更多解釋。

INSERT SQL 語句可以接收分隔的列和值,格式為 INSERT INTO table (column1Name) VALUES ('column1Value') .我們可以使用 join() 方法來生成用逗號分隔的一串列,並執行類似的操作以在 create() 中加入我們想要的所有值 功能。

UPDATE SQL 語句稍微複雜一些。它的形式是UPDATE table SET column1Name = 'column1Value' .所以我們需要在update()中新建一個數組 以這種格式和 join() 存儲值的函數 他們以後。

讓我們將所有數據庫函數保存在自己的文件中,persistence.js ,因此我們可以在調用 API 文件中的函數時添加一些上下文,如下所示:

const persistence = require('./persistence');
persistence.getAll();

Joi 架構

根據經驗,我們應該始終在用戶使用之前驗證用戶發送的內容,例如當用戶嘗試創建資源時。

一些軟件包使這項任務變得容易。我們將使用 Joi 完成驗證。

首先,我們需要定義資源的模式、屬性及其類型的定義。它讓我們想起了 SQL CREATE 我們之前定義的語句:

const Joi = require('joi');

const exoplanetSchema = Joi.object({
    id: Joi.number(),
    name: Joi.string().required(),
    year_discovered: Joi.number(),
    light_years: Joi.number(),
    mass: Joi.number(),
    link: Joi.string().uri()
})
options({ stripUnknown: true });

每種類型都會強制執行一些驗證。例如,link 屬性需要看起來像一個 URI , 和 namerequired() .

稍後我們可以使用 exoplanetSchema.validate(theObject) 來驗證資源 方法。此方法將返回一個帶有 error 的對象 帶有驗證錯誤的屬性(如果有)和 value 處理對象的屬性。我們將在創建和更新對象時使用此驗證。

為了增加我們 API 的健壯性,最好忽略並丟棄任何未包含在我們的模式中的額外屬性。這在上面的定義中通過設置 stripUnknown 來實現 true 的選項 .

REST API with Express

我們將使用 Express 包來創建我們的 REST API。正如我們剛剛看到的,我們還將使用 Joi 驗證資源。

讓我們設置一個普通的 Express 服務器:

const express = require('express');
const cors = require('cors');

const app = express();
app.use(cors());
app.use(express.json());

app 變量是我們的 API,暫時為空。 Express 允許通過使用中間件來擴展其功能,這些功能可以修改我們 API 的請求和響應。在本例中,我們使用了兩個中間件。

一、cors() 將允許其他瀏覽器應用程序調用我們的 API。這包括我們稍後可能用來測試 API 的 Swagger 編輯器。如果您想了解更多關於使用 Node.js 和 Express 處理 CORS 的信息,我們已經為您提供了保障。

其次,我們添加 express.json() 用於解析請求正文中的 JSON 對象的中間件。

現在讓我們向 API 添加一些端點。我們將從 post() 開始 和 put() ,因為他們使用 Joi 驗證在上一節中解釋:

app.post('/exoplanets', (req, res) => {
    delete req.body.id;
    const { error, value } = exoplanetSchema.validate(req.body);
    if(error)
        res.status(405).send(error.details[0].message);

    persistence.create(value);
    res.status(201);
});

app.put('/exoplanets/:id', (req, res) => {
    delete req.body.id;
    const { error, value } = exoplanetSchema.validate(req.body);
    if(error) {
        res.status(405).send(error.details[0].message);
    }

    persistence.get(req.params.id, (result) => {
        if(result) {
            persistence.update(req.params.id, value);
            res.status(201);
        } else {
            res.status(404);
        }
    });
});

Express 支持每個 HTTP 動詞一個函數,所以在這種情況下,我們有 post()put() 作為兩個函數。

在這兩個函數中,首先驗證資源,然後任何 error 返回給 API 客戶端。為了使這段代碼簡單,在這種情況下只返回第一個驗證錯誤。

put() 還通過嘗試從數據庫中獲取資源來檢查資源是否存在。只有存在時才會更新資源。

使用 post()put() 需要驗證的函數,讓我們處理 get() 用戶想要查看系外行星的方法,以及 delete() 用於從數據庫中刪除系外行星的函數:

app.get('/exoplanets', (req, res) => persistence.getAll((result) => res.send(result)));

app.get('/exoplanets/:id', (req, res) => {
    persistence.get(req.params.id, (result) => {
        if(result)
            res.send(result);
        else
            res.status(404);
    });
});

app.delete('/exoplanets/:id', (req, res) => {
    persistence.get(req.params.id, (result) => {
        if(result) {
            persistence.delete(req.params.id);
            res; 
        } else {
            res.status(404);
        }            
    });
});

定義完所有端點後,讓我們設置應用程序偵聽請求的端口:

app.listen(5000, () => {
    persistence.initialize();
    console.log("Exoplanets API listening at http://localhost:5000")
});

上面的回調在啟動服務器時只會被調用一次,所以它是 initialize() 的完美位置 數據庫。

結論

H2 是一個有用的數據庫服務器,高性能且易於使用。雖然它是一個 Java 包,但它也作為獨立服務器運行,因此我們可以在 Node.js 中使用它和 JDBC 包。

在本教程中,我們首先定義了一個簡單的 CRUD 來說明如何訪問數據庫以及哪些函數可用。之後,我們用 Express 定義了一個 REST API .這有助於我們更完整地了解如何接收資源並將其保存到 H2 .

儘管為簡潔起見省略了幾個概念,例如身份驗證和分頁,但本教程是開始使用 H2 的一個很好的參考 在我們的快遞 項目。


Tutorial JavaScript 教程
  1. div類型=隱藏+不隱藏

  2. 使用 NextJS 創建一個待辦事項應用程序

  3. 使用 Netlify 和 Mailgun 發送郵件

  4. [NextJS] SSR 時的平滑路由

  5. 如何在日期選擇器中將日期格式 (MM/DD/YY) 更改為 (YYYY-MM-DD)

  6. 使用 SvelteKit load() 函數避免細微的數據獲取錯誤

  7. MongoDB、Express、Vue 和節點。

  1. 模型包含選項將屬性作為具有定義別名的單個值而不是具有屬性的對象返回

  2. 什麼是變量?

  3. 停止請求加載 HTML 文件的腳本

  4. 註冊表單的 JavaScript 驗證 | HTML 示例代碼

  5. AWS Amplify 訂閱使用情況 / 3. 封閉式聊天

  6. 如何在構造函數中設置javascript私有變量?

  7. JavaScript 中的集合

  1. 300行重寫React Fiber核心算法

  2. 面試準備:什麼是堆棧?

  3. 高斯和,求解缺失數

  4. NlphoseBuilder :通過拖放創建 NLP 管道的工具