Sequelize 是一款優秀的數據庫 ORM 框架,支持 mysql、postgres、sqlite、mariadb、mssql。使用方法很是靈活多變,GitHub star 數目前 20k 左右,其周邊工具 sequelize-auto 可自動從數據庫生成模型文件,sequelize-cli 能夠依據模型文件建立數據庫,能力很是強大。html
上篇文章講解了 Sequelize 的基本使用,這篇文章從源碼角度的解析 Sequelize,讓你們用的明明白白!!node
連接mysql
以 mysql 爲例git
npm i -S sequelize mysql2
複製代碼
實例化 Sequelize 進行鏈接,參數在構造函數中配置github
const Sequelize = require('sequelize');
// Option 1: Passing parameters separately
const sequelize = new Sequelize('database', 'username', 'password', {
host: 'localhost',
dialect: 'mysql',
// 額外的配置...
});
// Option 2: Passing a connection URI
const sequelize = new Sequelize('postgres://user:pass@example.com:5432/dbname');
複製代碼
源碼中 Sequelize 構造函數,列舉經常使用的參數正則表達式
database
string
數據庫名username=null
string
用戶名password=null
string
密碼options={}
Object
配置options.host='localhost'
string
host 地址options.port=
number
數據庫 serve 端口options.username=null
string
用戶名,同上面用戶名options.password=null
string
密碼,同上面用戶名,只需傳其一options.database=null
string
數據庫名,同上options.dialect
string
要使用的數據庫類型,目前支持 mysql
, postgres
, sqlite
, mssql
.timezone='+00:00'
string
時區設置,默認中國時區須要變動爲"+08:00"
,若是有使用 NOW
函數必定要注意。The timezone used when converting a date from the database into a JavaScript date. The timezone is also used to SET TIMEZONE when connecting to the server, to ensure that the result of NOW, CURRENT_TIMESTAMP and other time related functions have in the right timezone. For best cross platform performance use the format +/-HH:MM. Will also accept string versions of timezones used by moment.js (e.g. 'America/Los_Angeles'); this is useful to capture daylight savings time changes.options.logging=console.log
Function
A function that gets executed every time Sequelize would log something.options.benchmark=false
boolean
Pass query execution time in milliseconds as second argument to logging function (options.logging).options.replication=false
boolean
Use read / write replication. To enable replication, pass an object, with two properties, read and write. Write should be an object (a single server for handling writes), and read an array of object (several servers to handle reads). Each read/write server can have the following properties: host
, port
, username
, password
, database
options.pool
Object
鏈接池配置options.pool.max=5
number
鏈接池最大鏈接數options.pool.min=0
number
鏈接池最小鏈接數options.pool.idle=10000
number
The maximum time, in milliseconds, that a connection can be idle before being released.閒置鏈接的最大存活時間options.pool.acquire=60000
number
The maximum time, in milliseconds, that pool will try to get connection before throwing error,出錯以後再次鏈接的最長時間options.pool.evict=1000
number
The time interval, in milliseconds, after which sequelize-pool will remove idle connections.多長時間以後將會移除閒置鏈接options.operatorsAliases
Object
String based operator alias. Pass object to limit set of aliased operators.設置操做符別名options.hooks
Object
鏈接和斷開數據庫的一些鉤子函數。 An object of global hook functions that are called before and after certain lifecycle events. Global hooks will run after any model-specific hooks defined for the same event (See Sequelize.Model.init()
for a list). Additionally, beforeConnect()
, afterConnect()
, beforeDisconnect()
, and afterDisconnect()
hooks may be defined here.// src/db/index.js
const Sequelize = require('sequelize');
const sequelize = new Sequelize('testdb', 'root', 'root', {
host: 'localhost',
port: 8889, // 默認是 3306,個人電腦設置的 8889
dialect: 'mysql',
});
module.exports = sequelize;
// App.js
const seq = require('./src/db');
seq
.authenticate()
.then(() => {
console.log('Connection has been established successfully.');
})
.catch(err => {
console.error('Unable to connect to the database:', err);
});
// node App.js
複製代碼
鏈接成功則打印以下sql
Sequelize 須要創建模型,才能夠對數據庫進行操做,對數據庫的每一個表創建模型文件太過繁瑣。 可使用 sequelize-auto 從數據庫直接導出模型。typescript
先建立數據庫 testdb,執行 sql 語句自動建表 testdb。數據庫
安裝 sequelize-autonpm
npm i -D sequelize-auto
複製代碼
sequelize-auto 使用
[node] sequelize-auto -h <host> -d <database> -u <user> -x [password] -p [port] --dialect [dialect] -c [/path/to/config] -o [/path/to/models] -t [tableName] -C
Options:
-h, --host IP/Hostname for the database. [required]
-d, --database Database name. [required]
-u, --user Username for database.
-x, --pass Password for database.
-p, --port Port number for database.
-c, --config JSON file for Sequelize's constructor "options" flag object as defined here: https://sequelize.readthedocs.org/en/latest/api/sequelize/
-o, --output What directory to place the models.
-e, --dialect The dialect/engine that you're using: postgres, mysql, sqlite
-a, --additional Path to a json file containing model definitions (for all tables) which are to be defined within a model's configuration parameter. For more info: https://sequelize.readthedocs.org/en/latest/docs/models-definition/#configuration
-t, --tables Comma-separated names of tables to import
-T, --skip-tables Comma-separated names of tables to skip
-C, --camel Use camel case to name models and fields
-n, --no-write Prevent writing the models to disk.
-s, --schema Database schema from which to retrieve tables
-z, --typescript Output models as typescript with a definitions file.
複製代碼
簡單配置
// package.json
"scripts": {
"sequelize": "sequelize-auto -o ./src/db/model -d testdb -h localhost -u root -p 8889 -x root -e mysql"
},
複製代碼
自動生成的模型以下
// src/db/model/blog.js
/* jshint indent: 2 */
module.exports = function(sequelize, DataTypes) {
return sequelize.define('blog', {
id: {
type: DataTypes.INTEGER(11),
allowNull: false,
primaryKey: true,
autoIncrement: true
},
school: {
type: DataTypes.STRING(255),
allowNull: true
},
name: {
type: DataTypes.STRING(255),
allowNull: true
}
}, {
tableName: 'blog',
timestamps: false,
});
};
// src/db/model/users.js
/* jshint indent: 2 */
module.exports = function(sequelize, DataTypes) {
return sequelize.define('users', {
id: {
type: DataTypes.INTEGER(11),
allowNull: false,
primaryKey: true,
autoIncrement: true
},
name: {
type: DataTypes.STRING(30),
allowNull: true
},
age: {
type: DataTypes.INTEGER(11),
allowNull: true
},
created_at: {
type: DataTypes.DATE,
allowNull: true
},
updated_at: {
type: DataTypes.DATE,
allowNull: true
}
}, {
tableName: 'users',
timestamps: false,
});
};
複製代碼
sequelize.define
與 model.init
源碼中 define
定義以下,它實際就是 model.init
,define
的三個參數被放到了 init
中。
define
上面有這句話,當 model 被 define 定義好以後,能夠經過 sequelize.models.modelName
來執行數據庫操做!!!
/** * Define a new model, representing a table in the database. * * The table columns are defined by the object that is given as the second argument. Each key of the object represents a column * * @param {string} modelName The name of the model. The model will be stored in `sequelize.models` under this name * @param {Object} attributes An object, where each attribute is a column of the table. See {@link Model.init} * @param {Object} [options] These options are merged with the default define options provided to the Sequelize constructor and passed to Model.init() * * @see * {@link Model.init} for a more comprehensive specification of the `options` and `attributes` objects. * @see <a href="/manual/tutorial/models-definition.html">Model definition</a> Manual related to model definition * @see * {@link DataTypes} For a list of possible data types * * @returns {Model} Newly defined model * * @example * sequelize.define('modelName', { * columnA: { * type: Sequelize.BOOLEAN, * validate: { * is: ["[a-z]",'i'], // will only allow letters * max: 23, // only allow values <= 23 * isIn: { * args: [['en', 'zh']], * msg: "Must be English or Chinese" * } * }, * field: 'column_a' * }, * columnB: Sequelize.STRING, * columnC: 'MY VERY OWN COLUMN TYPE' * }); * * sequelize.models.modelName // The model will now be available in models under the name given to define */
define(modelName, attributes, options = {}) {
options.modelName = modelName;
options.sequelize = this;
const model = class extends Model {};
model.init(attributes, options);
return model;
}
複製代碼
Sequelize.CHAR(100) // CHAR(100)
Sequelize.STRING // VARCHAR(255)
Sequelize.STRING(1234) // VARCHAR(1234)
Sequelize.TEXT // TEXT
Sequelize.TEXT('tiny') // TINYTEXT
複製代碼
Sequelize.TINYINT // TINYINT
Sequelize.SMALLINT // SMALLINT
Sequelize.MEDIUMINT // MEDIUMINT
Sequelize.INTEGER // INTEGER
Sequelize.BIGINT // BIGINT
Sequelize.BIGINT(11) // BIGINT(11)
Sequelize.FLOAT // FLOAT
Sequelize.FLOAT(11) // FLOAT(11)
Sequelize.FLOAT(11, 10) // FLOAT(11,10)
Sequelize.DOUBLE // DOUBLE
Sequelize.DOUBLE(11) // DOUBLE(11)
Sequelize.DOUBLE(11, 10) // DOUBLE(11,10)
Sequelize.DECIMAL // DECIMAL
Sequelize.DECIMAL(10, 2) // DECIMAL(10,2)
複製代碼
Sequelize.DATE // mysql / sqlite 爲 DATETIME, postgres 爲帶時區的 TIMESTAMP
Sequelize.DATE
Sequelize.TIME
Sequelize.DATEONLY // DATE 不帶時間.
複製代碼
Sequelize.BOOLEAN // TINYINT(1)
複製代碼
Sequelize.ENUM('value 1', 'value 2') // 一個容許值爲'value 1'和'value 2'的ENUM
複製代碼
Sequelize.BLOB // BLOB (PostgreSQL 爲 bytea)
Sequelize.BLOB('tiny') // TINYBLOB (PostgreSQL 爲 bytea. 其他參數是 medium 和 long)
複製代碼
Sequelize.GEOMETRY // Spatial 列. 僅 PostgreSQL (帶有 PostGIS) 或 MySQL.
Sequelize.GEOMETRY('POINT') // 帶有 geometry 類型的 spatial 列. 僅 PostgreSQL (帶有 PostGIS) 或 MySQL.
Sequelize.GEOMETRY('POINT', 4326) // 具備 geometry 類型和 SRID 的 spatial 列. 僅 PostgreSQL (帶有 PostGIS) 或 MySQL.
複製代碼
integer
, bigint
, float
和 double
還支持 unsigned 和 zerofill 屬性
Sequelize.INTEGER.UNSIGNED // INTEGER UNSIGNED
Sequelize.INTEGER(11).UNSIGNED // INTEGER(11) UNSIGNED
Sequelize.INTEGER(11).ZEROFILL // INTEGER(11) ZEROFILL
Sequelize.INTEGER(11).ZEROFILL.UNSIGNED // INTEGER(11) UNSIGNED ZEROFILL
Sequelize.INTEGER(11).UNSIGNED.ZEROFILL // INTEGER(11) UNSIGNED ZEROFILL
複製代碼
源代碼 sequelize/lib/sequelize.js
中,將 DataTypes 總體導出的同時,還將全部 DataTypes 類型所有掛載在了 Sequelize 上,因此有如下兩種使用方式
const Sequelize, { DataTypes } = require('sequelize')
// ...
created_at: {
type: Sequelize.DATE,
allowNull: true
}
// or
created_at: {
type: DataTypes.DATE,
allowNull: true
}
複製代碼
每一個字段的全部定義方式以下
type
string
| DataTypes
字符串或者 DataTypeallowNull=true
boolean
容許 NulldefaultValue=null
any
字段默認值unique=false
string
| boolean
是否惟一索引,If true, the column will get a unique constraint. If a string is provided, the column will be part of a composite unique index. If multiple columns have the same string, they will be part of the same unique indexprimaryKey=false
boolean
是否爲主鍵,If true, this attribute will be marked as primary keyautoIncrement=false
boolean
自增,If true, this column will be set to auto incrementcomment=null
string
字段註釋,Comment for this columnreferences=null
string
| Model
An object with reference configurationsreferences.model
string
| Model
If this column references another table, provide it here as a Model, or a stringreferences.key='id'
string
The column of the foreign table that this column referencesvalidate
Object
字段校驗,見屬性驗證器sequelize 模型須要手動配置一些參數 Configuration
其餘經常使用配置以下
timestamp=true
boolean
Sequelize 使用時會自動添加 createdAt
和 updatedAt
到模型中,若是表中沒有這兩個字段可是 timestamp=true
,則會報錯,須要在模型定義中指定爲 falsefreezeTableName=false
boolean
Sequelize 默認會將全部的表名變動爲複數,若是不想被自動變動,須要設置爲 truemodelName
string
模型名,默認是類名,以下代碼paranoid=false
boolean
調用 destroy
不會刪除記錄,可是會設置 deletedAt
字段爲當前的時間戳若是 paranoid=true
,須要 timestamps=true
纔會走這個邏輯underscored=false``boolean
不使用駝峯式命令規則,這樣會在使用下劃線分隔,updatedAt
的字段名會是 updated_at
tableName
string
表名,freezeTableName=false
會讓表名自動編程複數engine
string
表的引擎,默認爲 InnoDB
sequelize
Object
給模型傳入 sequelize 實例(new Sequelize(xxx)
),不傳入將會報錯class Bar extends Model {}
Bar.init({ /* bla */ }, {
// The name of the model. The model will be stored in `sequelize.models` under this name.
// This defaults to class name i.e. Bar in this case. This will control name of auto-generated
// foreignKey and association naming
modelName: 'bar',
// don't add the timestamp attributes (updatedAt, createdAt)
timestamps: false,
// don't delete database entries but set the newly added attribute deletedAt
// to the current date (when deletion was done). paranoid will only work if
// timestamps are enabled
paranoid: true,
// Will automatically set field option for all attributes to snake cased name.
// Does not override attribute with field option already defined
underscored: true,
// disable the modification of table names; By default, sequelize will automatically
// transform all passed model names (first parameter of define) into plural.
// if you don't want that, set the following
freezeTableName: true,
// define the table's name
tableName: 'my_very_custom_table_name',
// Enable optimistic locking. When enabled, sequelize will add a version count attribute
// to the model and throw an OptimisticLockingError error when stale instances are saved.
// Set to true or a string with the attribute name you want to use to enable.
version: true,
// Sequelize instance
sequelize,
})
複製代碼
前面咱們已經配置好了模型,創建好了數據鏈接,獲得 sequelize
實例和模型文件,這步咱們使用 sequelize.import
進行註冊。
import 方法會對註冊的 model 進行緩存,重複註冊相同 model 不會有效果。它能夠將 sequelize-auto
導出的 model 文件直接進行註冊。
官方文檔
源碼定義,源碼主要看 if (!this.importCache[importPath]) {
後面的,就是把 model 放到 this.importCache
上
咱們來實戰演示
基於數據庫導出的 users
和 blog
兩個 model,對 model 作如下更改,阻止默認行爲
// src/db/model/blog.js
{
tableName: 'blog',
timestamps: false,
}
// src/db/model/users.js
{
tableName: 'users',
timestamps: false,
}
複製代碼
修改 index.js
// src/db/index.js
const Sequelize = require('sequelize');
const fs = require('fs');
const path = require('path');
const sequelize = new Sequelize('testdb', 'root', 'root', {
host: 'localhost',
port: 8889,
dialect: 'mysql',
});
// model 目錄絕對路徑
const modelPath = path.resolve(__dirname, './model');
// 讀取全部 model 文件
const files = fs.readdirSync(modelPath);
const db = {};
// 將 model 掛到 db 上
files.forEach(fileName => {
const modelName = fileName.slice(0, -3);
db[modelName] = sequelize.import(path.resolve(modelPath, fileName));
});
module.exports = db;
複製代碼
App.js
const db = require('./src/db');
async function init() {
const users = await db.users.findAll();
const blog = await db.blog.findAll();
console.log(JSON.parse(JSON.stringify(users)));
console.log(JSON.parse(JSON.stringify(blog)));
}
init();
複製代碼
執行結果
更改 idle (閒置鏈接回收時間)
知道以上 sequelize 註冊方式以後,能夠很容易地把它嵌入到現有的 nodejs 框架中,掛載到上下文對象上便可在各個地方訪問。
建立一條記錄,並返回這條數據庫記錄
public static create(values: Object, options: Object): Promise<Model>
複製代碼
src/db/model/users.js 更改以下
created_at: {
type: DataTypes.DATE,
allowNull: true,
defaultValue: sequelize.fn('NOW') // 填充默認時間爲如今
},
updated_at: {
type: DataTypes.DATE,
allowNull: true,
defaultValue: sequelize.fn('NOW') // 填充默認時間爲如今
}
複製代碼
// App.js
const users = await db.users.create({
name: 'lxfriday',
age: 33,
});
console.log(JSON.parse(JSON.stringify(users)));
複製代碼
插入以後返回記錄
注意時區問題,在本篇文章最開始鏈接數據庫的時候 options.timezone
設爲 +08:00
便可變動爲中國時間。
建立多條記錄,並返回一個一個對象數組,每一個對象是一條記錄
public static bulkCreate(records: Array, options: Object): Promise<Array<Model>>
複製代碼
const users = await db.users.bulkCreate([
{
name: 'lxfriday1',
age: 111,
},
{
name: 'lxfriday2',
age: 222,
},
{
name: 'lxfriday3',
age: 338,
},
]);
複製代碼
先查找,沒有找到則依據條件進行建立
public static findOrCreate(options: Object): Promise<Model, boolean>
複製代碼
const users = await db.users.findOrCreate({
where: {
name: 'lxfridaysss',
age: 3399,
},
});
const users2 = await db.users.findOrCreate({
where: {
name: 'lxfridaywww',
},
defaults: {
age: 3399,
}
});
// 會自動合併成{name,age}
複製代碼
沒有找到的時候,會自動將 where 和 defaults 合併做爲新的數據源
按條件查詢全部
public static findAll(options: Object): Promise<Array<Model>>
複製代碼
主要注意查詢條件和查詢字段,操做符從 Sequelize.Op
導出。
where
Object
查詢條件attributes
Array<string> | Object
查詢的字段attributes.include
Array<string>
要包括的字段,同直接給 attributes
賦值attributes.exclude
Array<string>
不要要包括的字段order
Array | fn | col | literal
排序,[['id', 'DESC'], 'age']
['age', 'DESC'],
// ['id', 'ASC'],
'id',// 同上
複製代碼
limit
number
限制查詢條數offset
number
偏移量![](https://user-gold-cdn.xitu.io/2019/7/28/16c3936efb0f7f3e?w=548&h=501&f=png&s=62216)
複製代碼
await db.users.findAll({
where: {
attr1: 42,
attr2: 'cake'
}
})
// WHERE attr1 = 42 AND attr2 = 'cake'
複製代碼
const users = await db.users.findAll({
attributes: {
exclude: ['name']
},
where: {
name: {
[like]: '%lxfriday%',
},
},
order: [
['age', 'DESC'],
['id', 'ASC'],
// 'id',
],
});
複製代碼
執行語句和結果
const {gt, lte, ne, in: opIn} = Sequelize.Op;
await db.users.findAll({
where: {
attr1: {
[gt]: 50
},
attr2: {
[lte]: 45
},
attr3: {
[opIn]: [1,2,3]
},
attr4: {
[ne]: 5
}
}
})
// WHERE attr1 > 50 AND attr2 <= 45 AND attr3 IN (1,2,3) AND attr4 != 5
複製代碼
const {or, and, gt, lt} = Sequelize.Op;
await db.users.findAll({
where: {
name: 'a project',
[or]: [
{id: [1, 2, 3]},
{
[and]: [
{id: {[gt]: 10}},
{id: {[lt]: 100}}
]
}
]
}
});
// WHERE `Model`.`name` = 'a project' AND (`Model`.`id` IN (1, 2, 3) OR (`Model`.`id` > 10 AND `Model`.`id` < 100));
複製代碼
查找一條,是 findAll({limit: 1, ...})
版本,配置見 findAll
public static findOne(options: Object): Promise<Model>
複製代碼
public static findByPk(param: number | string | Buffer, options: Object): Promise<Model>
複製代碼
按主鍵進行查詢,主鍵能夠是任何任意字段和類型,不只僅是 id 才能當主鍵
blog_url
是主鍵
查到並統計結果數
public static findAndCountAll(options: Object): Promise<{count: number, rows: Model[]}>
複製代碼
更改數據
public static update(values: Object, options: Object): Promise<Array<number, number>>
複製代碼
返回一個數組,數組的參數是被修改的條數
更新,不存在則建立一條記錄
public static upsert(values: Object, options: Object): Promise<boolean>
複製代碼
public static destroy(options: Object): Promise<number>
複製代碼
public static count(options: Object): Promise<number>
複製代碼
field 要查詢的字段
public static min(field: string, options: Object): Promise<*>
複製代碼
public static aggregate(attribute: string, aggregateFunction: string, options: Object): Promise<DataTypes|Object>
複製代碼
從 sequelize.Op
導出,
const Op = Sequelize.Op
[Op.and]: {a: 5} // 且 (a = 5)
[Op.or]: [{a: 5}, {a: 6}] // (a = 5 或 a = 6)
[Op.gt]: 6, // id > 6
[Op.gte]: 6, // id >= 6
[Op.lt]: 10, // id < 10
[Op.lte]: 10, // id <= 10
[Op.ne]: 20, // id != 20
[Op.eq]: 3, // = 3
[Op.not]: true, // 不是 TRUE
[Op.between]: [6, 10], // 在 6 和 10 之間
[Op.notBetween]: [11, 15], // 不在 11 和 15 之間
[Op.in]: [1, 2], // 在 [1, 2] 之中
[Op.notIn]: [1, 2], // 不在 [1, 2] 之中
[Op.like]: '%hat', // 包含 '%hat'
[Op.notLike]: '%hat' // 不包含 '%hat'
[Op.iLike]: '%hat' // 包含 '%hat' (不區分大小寫) (僅限 PG)
[Op.notILike]: '%hat' // 不包含 '%hat' (僅限 PG)
[Op.startsWith]: 'hat' // 相似 'hat%'
[Op.endsWith]: 'hat' // 相似 '%hat'
[Op.substring]: 'hat' // 相似 '%hat%'
[Op.regexp]: '^[h|a|t]' // 匹配正則表達式/~ '^[h|a|t]' (僅限 MySQL/PG)
[Op.notRegexp]: '^[h|a|t]' // 不匹配正則表達式/!~ '^[h|a|t]' (僅限 MySQL/PG)
[Op.iRegexp]: '^[h|a|t]' // ~* '^[h|a|t]' (僅限 PG)
[Op.notIRegexp]: '^[h|a|t]' // !~* '^[h|a|t]' (僅限 PG)
[Op.like]: { [Op.any]: ['cat', 'hat']} // 包含任何數組['cat', 'hat'] - 一樣適用於 iLike 和 notLike
[Op.overlap]: [1, 2] // && [1, 2] (PG數組重疊運算符)
[Op.contains]: [1, 2] // @> [1, 2] (PG數組包含運算符)
[Op.contained]: [1, 2] // <@ [1, 2] (PG數組包含於運算符)
[Op.any]: [2,3] // 任何數組[2, 3]::INTEGER (僅限PG)
[Op.col]: 'user.organization_id' // = 'user'.'organization_id', 使用數據庫語言特定的列標識符, 本例使用 PG
複製代碼
廣告時間
歡迎關注,每日進步!!!