利用 Sequelize 來運算元據庫

Yzz發表於2019-03-01

利用傳說中的ORM技術,把關聯式資料庫的表結構對映到物件上,簡化資料庫操作。

Published: 2019-3-01

Sequelize 是一個基於 Prom 的 ORM for Node,面向熟悉 JavaScript 和使用 Node.js 進行後端開發的開發人員。在本篇文章中,將探討 Sequelize 的一些常見用例以及 利用 Sequelize-cli 完成相關的環境配置。

  1. Sequelize 極簡入門教程;
  2. Sequelize-cli 完成 dev,test,prod 環境的配置,以及資料庫建立;
  3. Sequelize-cli 完成表結構的設計、遷移與資料填充;
  4. Sequelize 結合 Sequelize-cli 完成資料庫的增、刪、改、查;
  5. 總結。

Sequelize 極簡入門教程

本章程式碼,here

ORM:Object-Relational Mapping,就是允許將關聯式資料庫對映到物件上,使得這些物件具有一些屬性和運算元據庫的方法,避免編寫 SQL 語句。

Sequelize:Sequelize 是一款基於 Nodejs 功能強大的非同步ORM框架,同時支援 PostgreSQL,MySQL,SQLite 等多種資料庫,很適合作為Nodejs後端資料庫的儲存介面。

本節簡單利用 Sequelize 向資料庫中插入一條資料,方便後續理解 Sequelize-cli。

安裝


可以利用 npmyarn 完成安裝

// Using NPM
$ npm install --save sequelize

# And one of the following:
$ npm install --save pg pg-hstore
$ npm install --save mysql2
$ npm install --save sqlite3
$ npm install --save tedious // MSSQL

// Using Yarn
$ yarn add sequelize

# And one of the following:
$ yarn add pg pg-hstore
$ yarn add mysql2
$ yarn add sqlite3
$ yarn add tedious // MSSQL
複製程式碼

本文依賴 mysql,所以

$ npm install --save sequelize
$ npm install --save mysql2
複製程式碼

建立與資料庫連線


Sequelize 提供了兩種連線資料庫的方式

const Sequelize = require('sequelize');
// 資料庫相關引數
const sequelize = new Sequelize('database', 'username', 'password', {
    // 所在ip
    host: 'localhost',
    // 所用埠
    port: '埠',
    // 所用資料庫型別
    dialect: 'mysql'|'sqlite'|'postgres'|'mssql',
    // 請參考 Querying - 查詢 操作符 章節
    operatorsAliases: false,
    // 設定連線池,因此如果您從單個程式連線到資料庫,理想情況下應該只為每個資料庫建立一個例項
    pool: {
        max: 5,
        min: 0,
    	acquire: 30000,
    	idle: 10000
  	},
    // 執行過程會log一些SQL的logging,設為false不顯示
  	logging: false,

  	// SQLite only
  	storage: 'path/to/database.sqlite'
});

// 利用 uri 簡易連線資料庫
const sequelize = new Sequelize('postgres://user:pass@example.com:5432/dbname');
複製程式碼

本文所用資料庫為 mysql,結合 sequelize.authenticate 來對連線進行測試,構建 index.js

const Sequelize = require('sequelize');

const sequelize = new Sequelize('users_dev', 'username', 'password', {
    host: 'localhost',
    port: 3306,
    dialect: 'mysql',
    operatorsAliases: false,
    // logging: false,

    pool: {
        max: 5,
        min: 0,
        acquire: 30000,
        idle: 10000
    },
});

sequelize
    .authenticate()
    .then(() => {
        console.log('Connection has been established successfully.');
        process.exit();
    })
    .catch(err => {
        console.error('Unable to connect to the database:', err);
    });
複製程式碼

利用 node index.js 執行該指令碼,成功的會列印出 Connection has been established successfully.

定義 Model,並插入資料


Model 主要是用來完成與表之間的對映,主要利用 sequelize.define('name', {attributes}, {options}) 完成 Model 的定義。我們定義一個 User 模型對應 user 表。

const User = sequelize.define('user', {
    // 即使表的結構也是Model的屬性
    firstName: {
        type: Sequelize.STRING
    },
    lastName: {
        type: Sequelize.STRING
    }
});
複製程式碼

利用已經定義好的Model,可以完成對 user 表的插入資料操作

// force: true will drop the table if it already exists
User.sync({
    force: true
}).then(() => {
    // Table created
    return User.create({
        firstName: 'John',
        lastName: 'Hancock'
    });
}).then(() => {
    process.exit()
})
複製程式碼

以上完成 Sequelize 的極簡介紹,主要想介紹一個對映的流向,方便後續理解,官網例項更加詳細

Sequelize-cli 完成 dev,test,prod 環境的配置,以及資料庫建立

本章程式碼,here

與 Sequelize 相伴的有 Sequelize-cli 工具,Sequelize-cli 為我們提供了一系列好用的終端指令,來完成以下工作

  • 配置不同的環境的資料庫連線,例如dev、test、prod等;
  • 自動管理表對應的 Model;
  • 利用 migrations 完成資料庫的表結構的遷移;
  • 利用 seeders 完成資料庫的表內容的初始化。

首先安裝 Sequelize-cli

npm i sequelize-cli -D
複製程式碼

在 package.json 中新增

"scripts": {
    "init": "node_modules/.bin/sequelize init",
    ...
}
複製程式碼

執行 npm run init 命令,之後會發現,在目錄下多了 config、models、migrations、seeders四個資料夾

├── config # 專案配置目錄 | ├── config.json # 資料庫連線的配置 ├── models # 資料庫 model | ├── index.js # 資料庫連線的樣板程式碼 ├── migrations # 資料遷移的目錄 ├── seeders # 資料填充的目錄

本節只考慮配置相關的,也就是config資料夾下的內容,主要包含 config.json

{
    "development": {
        "username": "root",
        "password": null,
        "database": "database_development",
        "host": "127.0.0.1",
        "dialect": "mysql"
    },
    "test": {
        "username": "root",
        "password": null,
        "database": "database_test",
        "host": "127.0.0.1",
        "dialect": "mysql"
    },
    "production": {
        "username": "root",
        "password": null,
        "database": "database_production",
        "host": "127.0.0.1",
        "dialect": "mysql"
    }
}
複製程式碼

主要包含了 development、test、production,三個環境下的資料庫資訊。

之前我也是利用 config.json 來管理的,但是之後通過閱讀基於 hapi 的 Node.js 小程式後端開發實踐指南,發現利用 .env 檔案來管理是一種更為優雅的方法。

安裝 env2 外掛,在當前目錄下建立 .env 檔案用於配置開發環境以及生產環境的基礎資訊。

npm i -D env2
複製程式碼

.env 內容,注字串變數不需要''。

DB_USERNAME = username
DB_PASSWORD = password
DB_NAME = dataname
DB_NAME_PROD = prodDataname
DB_HOST = *.*.*.*
DB_PORT = *
複製程式碼

如果 git 非私密的,需要配置 .gitignore 的相關資訊,在config檔案下,建立config.js

require('env2')('./.env');

const {
    env
} = process;

module.exports = {
    "development": {
        "username": env.DB_USERNAME,
        "password": env.DB_PASSWORD,
        "database": env.DB_NAME,
        "host": env.DB_HOST,
        "port": env.DB_PORT,
        "dialect": "mysql",
        "operatorsAliases": false,
    },
    "production": {
        "username": env.DB_USERNAME,
        "password": env.DB_PASSWORD,
        "database": env.DB_NAME_PROD,
        "host": env.DB_HOST,
        "port": env.DB_PORT,
        "dialect": "mysql",
        "operatorsAliases": false,
    }
}
複製程式碼

同時修改models資料夾下的index.js

// .json => .js
const config = require(__dirname + '/../config/config.js')[env];
複製程式碼

以上利用env2完成對開發環境,生產環境的config配置,新增 create 以及 create:prod 兩條指令

"scripts": {
    "init": "node_modules/.bin/sequelize init",
    "create": "node_modules/.bin/sequelize db:create",
    "create:prod": "node_modules/.bin/sequelize db:create --env production",
    ...
}
複製程式碼

可建立開發環境、生產環境的的資料庫。

Sequelize-cli 完成表結構的設計、遷移與資料填充

本章程式碼,here

表結構的設計、遷移都與 Migrations 相關

就像使用Git / SVN管理原始碼中的更改一樣,可以使用 Migration 來初始化資料庫、或跟蹤資料庫更改,也就是說通過配置 Migration 檔案可以將現有資料庫遷移至另一個狀態,並且儲存記錄。

"scripts": {
	...
	"migration": "node_modules/.bin/sequelize migration:create --name create-examples-table",
	"migration:prod": "node_modules/.bin/sequelize migration:create --name create-examples-table --env production"
	...
}
複製程式碼

首先在開發環境下進行測試,執行 npm run migration 指令,之後會在 migrations 資料夾內建立一個20190301054713-create-examples-table.js 檔案,內容為

'use strict';

module.exports = {
  up: (queryInterface, DataTypes) => {
    /*
      Example:
      return queryInterface.createTable('users', { id: DataTypes.INTEGER });
    */
  },

  down: (queryInterface, DataTypes) => {
    /*
      Example:
      return queryInterface.dropTable('users');
    */
  }
};
複製程式碼

模組暴漏出一個物件,包含著 updown 兩個方法,up 用於定義表結構正向改變,down 則用於定義表結構的回退,對應其中的 return ,正向 createTable ,反向則是 dropTable

兩個引數的定義:

  • queryInterface:用於定義Sequelize與所屬資料庫通訊的介面,包含一些API,例如createTable用於建立表,dropTable則用於撤銷,addColumn用於追加欄位,removeColumn則用於移除;

  • DataTypes:用於定義介面資料的型別。

queryInterface.createTable(...) 整體功能與 sequelize.define(...) 類似。簡單設計如下表

'use strict';

module.exports = {
    up: (queryInterface, DataTypes) => {
        return queryInterface.createTable('users', {
            id: {
                type: DataTypes.INTEGER,
                autoIncrement: true,
                primaryKey: true,
            },
            name: {
                type: DataTypes.STRING,
                allowNull: false,
            },
            created_at: DataTypes.DATE,
            updated_at: DataTypes.DATE,
        });
    },

    down: (queryInterface) => {
        return queryInterface.dropTable('users');
    }
};
複製程式碼

新增如下指令

"scripts": {
	...
	"migrate": "node_modules/.bin/sequelize db:migrate",
    "migrate:prod": "node_modules/.bin/sequelize db:migrate --env production",
	...
}
複製程式碼

執行 npm run migrate ,會將 migrations 目錄下的遷移行為定義,按時間戳的順序,逐個地執行遷移描述,最終完成資料庫表結構的自動化建立。會發現資料庫examples_dev內建立了一張 SequelizeMeta 的表以及 users 的表:

  • SequelizeMeta:記錄了對應遷移檔案的資訊;
  • users:是利用 queryInterface.createTable 建立的表。

相應的也有 node_modules/.bin/sequelize db:migrate:undo 來撤銷相應的遷移,這裡就不展開介紹了。

資料填充


主要利用 seeders 來在初始化資料表中中初始化一些基礎資料,使用方式與資料庫表結構遷移相似,新增如下指令。

"scripts": {
	...
	"seeder": "node_modules/.bin/sequelize seed:create --name init-users",
    "seeder:prod": "node_modules/.bin/sequelize seed:create --name init-users --env production",
	...
}
複製程式碼

執行 npm run seed 指令,則與資料遷移相同的是,seeders 資料夾下多了一個 ***init-users.js 檔案,結構也和資料遷移類似。

'use strict';

module.exports = {
    up: (queryInterface, Sequelize) => {
        /*
          Example:
          return queryInterface.bulkInsert('People', [{
            name: 'John Doe',
            isBetaMember: false
          }], {});
        */
    },

    down: (queryInterface, Sequelize) => {
        /*
          Example:
          return queryInterface.bulkDelete('People', null, {});
        */
    }
};
複製程式碼

引數也相同,只不過一個是建立表,一個是建立資料,所利用的API不同而已,例如。

'use strict';

module.exports = {
    up: (queryInterface, Sequelize) => {
        return queryInterface.bulkInsert('users', [{
            name: 'John Doe',
            created_at: new Date(),
            updated_at: new Date()
        }], {});
    },

    down: (queryInterface, Sequelize) => {
        return queryInterface.bulkDelete('users', null, {});
    }
};
複製程式碼

新增指令

"scripts": {
    ...
    "seed": "node_modules/.bin/sequelize db:seed:all",
    "seed:prod": "node_modules/.bin/sequelize db:seed:all --env production",
    ...
}
複製程式碼

也可以用 node_modules/.bin/sequelize db:seed --seed xxxxxxxxx-init-users.js 來指定添充資料。

Sequelize 結合 Sequelize-cli 完成資料庫的增、刪、改、查

本章程式碼,here

在第一節中,簡單介紹了 User.create(...) 插入了一條資料,本節中介紹結合 Sequelize-cli 完成對資料庫的增、刪、改、查。

在 Models 資料夾下建立對應的模型檔案 users.js,內容與第一節 sequelize.define(...) 類似

module.exports = (sequelize, DataTypes) => sequelize.define(
    'users', {
        id: {
            type: DataTypes.INTEGER,
            autoIncrement: true,
            primaryKey: true,
        },
        name: {
            type: DataTypes.STRING,
            allowNull: false,
        }
    }, {
        tableName: 'users',
        // 以下兩個屬性是針對createAt、updateAt這兩個預設屬性的,timestamps是不使用,而underscored
        // 則是將createAt轉化為create_at
        // timestamps: false,
        underscored: true,
    }
)
複製程式碼

模型結構,與資料遷移相同,在 index.js 檔案內引入模型

const { users } = require("./models");
複製程式碼

可以利用該 Model 完成對錶 users 的操作,主要以下幾個

  • 查:findAllfindByPkfindCreateFindfindOrCreate.....

    const { users } = require("./models");
    
    (async () => {
        // 搜尋多個例項
        const user = await users.findAll()
    	// 條件搜尋name = 'John Doe'
        // const user = await users.findByPk(1)
    
        console.log(user)
    
        process.exit();
    })()
    複製程式碼
  • 增:createbulkCreate....

    const { users } = require("./models");
    
    (async () => {
        await users.create({
            name: 'Yang'
        })
    
        process.exit();
    })()
    複製程式碼
  • 刪:destroydrop刪表.....

    const { users } = require("./models");
    
    (async () => {
        await users.destroy({
            where: {
                name: 'Yang'
            }
        })
    
        process.exit();
    })()
    複製程式碼
  • 改:upsertupdate.....

    const { users } = require("./models");
    
    (async () => {
        await users.update({
            name: 'Yange'
        }, {
            where: {
                name: 'John Doe'
            }
        })
    /*    
        await users.upsert({
            name: 'Sccc'
        }, {
            where: {
                name: 'Yange'
            }
        })
    */
        process.exit();
    })()
    複製程式碼

總結


這篇主要是用來總結之前一直看到的零散知識,也是用來配置存底,防止自己忘了。之前用的 mongoose 到 Sequelize,感覺自己一直在切換工具,卻又沒有很好地入門瞭解。之後應該是會選擇一個點深入瞭解下吧,對自己很是失望。

參考

http://docs.sequelizejs.com/class/lib/model.js~Model.html#static-method-upsert

https://juejin.im/book/5b63fdba6fb9a04fde5ae6d0/section/5b6c048e6fb9a04fdc36afc1

相關文章