Sequelize 是一个基于 promise 的 Node.js ORM, 目前支持 Postgres, MySQL, SQLite 和 Microsoft SQL Server. 它具有强大的事务支持, 关联关系, 读取和复制等功能[2]. 下面主要介绍如何使用sequelize-cli工具快速构建nodejs的数据访问层。
基于express创建nodejs web应用
基于express建立nodejs的web应用,在此基础上再使用sequlize建立数据访问层和数据库。
1 2 3 4
| mkdir myapp cd myapp express npm install
|
基于sequelize创建数据访问层和数据库
安装sequlize
1 2
| npm install --save sequelize npm install --save mysql2
|
安装sequelize-cli
首先应安装sequelize-cli工具,可以选择全局安装,也可以选择本地安装。
1 2
| npm install --save-dev sequelize-cli npm install -g sequelize-cli
|
建立初始的ORM引导框架
使用如下命令在Nodejs Web应用中建立初始的数据访问层。
1 2 3
| mkdir db cd db ../node_modules/.bin/sequelize init
|
这将创建以下文件夹:
- config, 包含配置文件,它告诉CLI如何连接数据库
- models,包含您的项目的所有模型
- migrations, 包含所有迁移文件
- seeders, 包含所有种子文件
修改配置文件以连接到数据库管理系统,并创建数据库
在建立模型之前,应先修改config/config.json,以告诉 CLI 如何连接到数据库。config/config.json内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| { "development": { "username": "root", "password": "root", "database": "database_development", "host": "127.0.0.1", "dialect": "mysql", "pool":{ "max": 5, "min": 0, "idle": 10000 }, "timezone": "+08:00", "define":{ "charset": "utf8", "dialectOptions":{ "collate": "utf8_general_ci" } } }, "test": { "username": "root", "password": "root", "database": "database_test", "host": "127.0.0.1", "dialect": "mysql" }, "production": { "username": "root", "password": "root", "database": "database_test", "host": "127.0.0.1", "dialect": "mysql", "pool":{ "max": 5, "min": 0, "idle": 10000 }, "timezone": "+08:00", "define":{ "charset": "utf8", "dialectOptions":{ "collate": "utf8_general_ci" } } } }
|
上述配置修改了数据库的字符集,使之能支持中文。当然在创建数据库之前,应配置mysql数据库管理系,使其支持utf8字符集。可执行如下命令查看其支持的字符集,如果其不支持uft字符集,请按照参考链接[3]进行修改。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| mysql> show variables like '%char%'; +--------------------------+---------------------------------------------------------+ | Variable_name | Value | +--------------------------+---------------------------------------------------------+ | character_set_client | utf8 | | character_set_connection | utf8 | | character_set_database | utf8 | | character_set_filesystem | binary | | character_set_results | utf8 | | character_set_server | utf8 | | character_set_system | utf8 | | character_sets_dir | C:\Program Files\MySQL\MySQL Server 5.5\share\charsets\ | +--------------------------+---------------------------------------------------------+ 8 rows in set (0.00 sec)
|
使用如下命令创建数据库:
1
| ../node_modules/.bin/sequelize db:create
|
使用如下命令删除数据库:
1
| ../node_modules/.bin/sequelize db:drop
|
创建模型
我们将使用 model:generate 命令。 此命令需要两个选项:
- name, 模型的名称
- attributes, 模型的属性列表
创建一个名叫 User 的模型:
1
| ../node_modules/.bin/sequelize model:generate --name User --attributes firstName:string,lastName:string,email:string
|
这将发生以下事情:
- 在 models 文件夹中创建了一个 user 模型文件
- 在 migrations 文件夹中创建了一个名字像 XXXXXXXXXXXXXX-create-user.js 的迁移文件
注意: _Sequelize 将只使用模型文件,它是表描述。另一边,迁移文件是该模型的更改,或更具体的是说 CLI 所使用的表。 处理迁移,如提交或日志,以进行数据库的某些更改。
再创建一个名为Role的模型,它跟User是一对多的关系:
1
| ../node_modules/.bin/sequelize model:generate --name Role --attributes roleName:string
|
定义关系
Role和User是一对多的关系,因此需要修改它们的模型定义。
修改models/role.js如下:
1 2 3 4 5 6 7 8 9 10 11
| 'use strict'; module.exports = (sequelize, DataTypes) => { var Role = sequelize.define('Role', { roleName: DataTypes.STRING }, {}); Role.associate = function(models) { // associations can be defined here Role.hasMany(models.User) }; return Role; };
|
修改models/user.js如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 'use strict'; module.exports = (sequelize, DataTypes) => { var User = sequelize.define('User', { firstName: DataTypes.STRING, lastName: DataTypes.STRING, email: DataTypes.STRING }, {}); User.associate = function(models) { // associations can be defined here User.belongsTo(models.Role, { onDelete: "NULL", foreignKey: { allowNull: false } }) }; return User; };
|
修改和运行迁移
Role和User是一对多的关系,因此需要修改User迁移文件的定义。
修改migrates/20180622153152-create-user.js如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| 'use strict'; module.exports = { up: (queryInterface, Sequelize) => { return queryInterface.createTable('Users', { id: { allowNull: false, autoIncrement: true, primaryKey: true, type: Sequelize.INTEGER }, roleId: { // name of the key we're adding type: Sequelize.INTEGER, allowNull: false, references: { model: 'Roles', // name of Target model key: 'id', // key in Target model that we're referencing }, onUpdate: 'CASCADE', onDelete: 'CASCADE', }, firstName: { type: Sequelize.STRING }, lastName: { type: Sequelize.STRING }, email: { type: Sequelize.STRING }, createdAt: { allowNull: false, type: Sequelize.DATE }, updatedAt: { allowNull: false, type: Sequelize.DATE } }); }, down: (queryInterface, Sequelize) => { return queryInterface.dropTable('Users'); } };
|
注意:这里主要给User添加了外键字段,外键字段命名有规则:通常为小写的模型名加Id,即驼峰风格。例如这里加了roleId的外键字段。
直到目前为止,CLI没有将任何东西插入数据库。 刚刚为模型 User和Role创建了必需的模型和迁移文件。 现在要在数据库中实际创建该表,需要运行 db:migrate 命令。
1
| ../node_modules/.bin/sequelize db:migrate
|
此命令将执行这些步骤:
- 将在数据库中创建一个名为 SequelizeMeta 的表。 此表用于记录在当前数据库上运行的迁移
- 开始寻找尚未运行的任何迁移文件。 这可以通过检查 SequelizeMeta 表。 在这个例子中,它将运行创建的 XXXXXXXXXXXXXX-create-role.js和XXXXXXXXXXXXXX-create-user.js 迁移。
- 创建一个名为 Roles 的表,其中包含其迁移文件中指定的所有列。
- 创建一个名为 Users 的表,其中包含其迁移文件中指定的所有列。
除了使用sequelize db:migrate命令创建数据库表之外,可以使用models同步创建或删除数据库表,例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| if (appConfig.dbCreated === false) { models.sequelize.sync().then(function() { //保存应用配置 appConfig.dbCreated = true; fs.writeFile(__dirname + '/../config/appConfig.json', JSON.stringify(appConfig), function(err) { if (err) return console.error(err); console.log('app config save is done'); }); Promise.all([ models.T_Role.create({ID:pwdHash.createMd5(Math.random().toString()),Role_Name:'管理员'}), models.T_User.create({ID:pwdHash.createMd5(Math.random().toString()),Account:appConfig.adminAccount,Password:pwdHash.createHash(appConfig.adminPassword),Audit:true,Avatar:'/avatar/default.jpg'}), models.T_Role.create({ID:pwdHash.createMd5(Math.random().toString()),Role_Name:'普通用户'}) ]).then(function (result){ //console.log(result); var role1=result[0]; var user1=result[1]; role1.setT_Users(user1).catch(err=>console.log(err)); }).catch(err=>console.log(err)); server.listen(port); server.on('error', onError); server.on('listening', onListening); }); } else { server.listen(port); server.on('error', onError); server.on('listening', onListening); }
|
创建种子,生成测试数据
假设我们希望在默认情况下将一些数据插入到几个表中。 例如创建几个用户和角色:
1 2
| ../node_modules/.bin/sequelize seed:generate --name demo-role ../node_modules/.bin/sequelize seed:generate --name demo-user
|
这个命令将会在 seeders 文件夹中创建两个种子文件。文件名看起来像是 XXXXXXXXXXXXXX-demo-role.js和XXXXXXXXXXXXXX-demo-user.js,它遵循相同的 up/down 语义,如迁移文件。
现在我们应该编辑这两个文件,将演示角色插入Role表,将演示用户插入User表。修改XXXXXXXXXXXXXX-demo-role.js如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| 'use strict';
module.exports = { up: (queryInterface, Sequelize) => { return queryInterface.bulkInsert('Roles', [{ roleName: '管理员', createdAt:new Date(), updatedAt:new Date() },{ roleName: '普通用户', createdAt:new Date(), updatedAt:new Date() }], {});
},
down: (queryInterface, Sequelize) => { return queryInterface.bulkDelete('Roles', null, {});
} };
|
修改XXXXXXXXXXXXXX-demo-user.js如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| 'use strict';
module.exports = { up: (queryInterface, Sequelize) => { return queryInterface.bulkInsert('Users', [{ roleId:1, firstName: 'John', lastName: 'Doe', email: 'demo@demo.com', createdAt:new Date(), updatedAt:new Date() },{ roleId:1, firstName: 'Jack', lastName: 'Smith', email: 'jack@demo.com', createdAt:new Date(), updatedAt:new Date() }], {});
},
down: (queryInterface, Sequelize) => { return queryInterface.bulkDelete('Users', null, {}); } };
|
种子文件修改后,即可使用如下命令将演示数据插入数据库中:
1
| ../node_modules/.bin/sequelize db:seed:all
|
工程实践经验总结
在工程实践中,只需使用sequelize-cli创建数据库,生成models即可,然后使用代码直接根据models创建数据库表,并生成测试数据。
参考链接
- Sequelize Reference,by sequelizejs.
- Sequelize 中文文档,by demopark.
- 解决Mysql中文乱码问题, by 最怕认真.
- How to set primary key type to UUID via Sequelize CLI,by stackoverflow.
- 数据库表主键设计方法,by jackhuang.
- Variable,by sequelize.
- cascade delete does not work,by sequelize.
- Adding foreign key to a table using Sequelize,by Nathan Sebhastian.
- Eager Loading - 预先加载,by demopark.