Sequelize-Automate: 自动生成 Sequelize Models
来源:SegmentFault
时间:2023-01-26 21:42:00 103浏览 收藏
小伙伴们对数据库编程感兴趣吗?是否正在学习相关知识点?如果是,那么本文《Sequelize-Automate: 自动生成 Sequelize Models》,就很适合你,本篇文章讲解的知识点主要包括MySQL、前端、Node.js、javascript、sequelize。在之后的文章中也会多多分享相关知识点,希望对大家的知识积累有所帮助!
本文的主角是 sequelize-automate
背景
Sequelize 是 Node.js 中常用的 ORM 库,其作用就是对数据库表和代码中的对象做一个映射,让我们能够通过面向对象的方式去查询和操作数据库。
举个例子,数据库可能有一张
class UserModel extends Model {} User.init({ id: DataTypes.INTEGER, name: DataTypes.STRING, birthday: DataTypes.DATE }, { sequelize, modelName: 'userModel' });
然后可以通过
CREATE TABLE `user` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary ket', `name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'user name', `email` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'user email', `created_at` datetime NOT NULL COMMENT 'created datetime', `updated_at` datetime NOT NULL COMMENT 'updated datetime', PRIMARY KEY (`id`), UNIQUE KEY `uk_name` (`name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='User table'
则使用 sequelize-automate 可以自动生成的 Model 文件为
const { DataTypes } = require('sequelize'); module.exports = sequelize => { const attributes = { id: { type: DataTypes.INTEGER(11).UNSIGNED, allowNull: false, defaultValue: null, primaryKey: true, autoIncrement: true, comment: "primary key", field: "id" }, name: { type: DataTypes.STRING(100), allowNull: false, defaultValue: null, primaryKey: false, autoIncrement: false, comment: "user name", field: "name", unique: "uk_name" }, email: { type: DataTypes.STRING(255), allowNull: false, defaultValue: null, primaryKey: false, autoIncrement: false, comment: "user email", field: "email" }, created_at: { type: DataTypes.DATE, allowNull: false, defaultValue: null, primaryKey: false, autoIncrement: false, comment: "created datetime", field: "created_at" }, updated_at: { type: DataTypes.DATE, allowNull: false, defaultValue: null, primaryKey: false, autoIncrement: false, comment: "updated datetime", field: "updated_at" } }; const options = { tableName: "user", comment: "", indexes: [] }; const UserModel = sequelize.define("user_model", attributes, options); return UserModel; };
这样我们就可以在项目中直接使用了:
const Sequelize = require('sequelize'); const UserModel = require('./models/user'); // Option 1: Passing parameters separately const sequelize = new Sequelize('database', 'username', 'password', { host: 'localhost', dialect: /* one of 'mysql' | 'mariadb' | 'postgres' | 'mssql' | 'sqlite' */ }); const userModel = UserModel(sequelize); const users = await userModel.findAll();
Sequelize-Automate 使用
$ npm install -g sequelize-automate
然后还需要安装使用的数据库对应的依赖包,这点与 sequelize 一致:
# 根据你使用的数据库,从下面的命令中选一个安装即可 $ npm install --save pg pg-hstore # Postgres $ npm install --save mysql2 $ npm install --save mariadb $ npm install --save sqlite3 $ npm install --save tedious # Microsoft SQL Server
当然,如果你已经在项目中使用了 sequelize,则一定会安装一个对应的数据库依赖包。
安装成功后,你就可以在项目目录中通过
"scripts": { "sequelize-automate": "sequelize-automate" },
这样就可以通过
sequelize-automate -t js -h localhost -d test -u root -p root -P 3306 -e mysql -o models
如果在项目中使用的话,则可以将改命令添加到 package.json 中:
"scripts": { "sequelize-automate": "sequelize-automate -t js -h localhost -d test -u root -p root -P 3306 -e mysql -o models" },
然后通过
{ "dbOptions": { "database": "test", "username": "root", "password": "root", "dialect": "mysql", "host": "localhost", "port": 3306, "logging": false }, "options": { "type": "js", "dir": "models" } }
当然也可以使用 JS 文件:
module.exports = { dbOptions: { database: "test", username: "root", password: "root", dialect: "mysql", host: "localhost", port: 3306, logging: false }, options: { type: "js", dir: "models" } }
然后就可以通过
dbOptions: { database: 'test', username: 'root', password: 'root', dialect: 'mysql', host: '127.0.0.1', port: 3306, define: { underscored: false, freezeTableName: false, charset: 'utf8mb4', timezone: '+00:00', dialectOptions: { collate: 'utf8_general_ci', }, timestamps: false, }, };
通常我们会用到的就是
options: { type: 'js', // 指定 models 代码风格 camelCase: false, // Models 文件中代码是否使用驼峰发命名 fileNameCamelCase: true, // Model 文件名是否使用驼峰法命名,默认文件名会使用表名,如 `user_post.js`;如果为 true,则文件名为 `userPost.js` dir: 'models', // 指定输出 models 文件的目录 typesDir: 'models', // 指定输出 TypeScript 类型定义的文件目录,只有 TypeScript / Midway 等会有类型定义 emptyDir: false, // 生成 models 之前是否清空 `dir` 以及 `typesDir` tables: null, // 指定生成哪些表的 models,如 ['user', 'user_post'];如果为 null,则忽略改属性 skipTables: null, // 指定跳过哪些表的 models,如 ['user'];如果为 null,则忽略改属性 tsNoCheck: false, // 是否添加 `@ts-nocheck` 注释到 models 文件中 }
所有参数可以参考源码:src/index.js#L13。
这里补充一点,之所以有
// @ts-nocheck import { IApplicationContext, providerWrapper } from 'midway'; import { DataTypes } from 'sequelize'; import { IDB } from './db'; export default async function setupModel(context: IApplicationContext) { const db: IDB = await context.getAsync('DB'); const attributes = { id: { type: DataTypes.BIGINT.UNSIGNED, allowNull: false, defaultValue: null, primaryKey: true, autoIncrement: true, comment: '主键', field: 'id', }, name: { type: DataTypes.STRING(100), allowNull: false, defaultValue: null, primaryKey: false, autoIncrement: false, comment: null, field: 'name', }, }; const options = { tableName: 'flow', comment: '', indexs: [], }; return db.sequelize.define('userModel', attributes, options); } providerWrapper([{ id: 'UserModel', provider: setupModel, }]);
API
上面主要讲了 sequelize-automate 的命令行使用方式,sequelize-automate 本身也提供了接口,让使用者自定义开发。主要有两个:
-
const Automate = require('sequelize-automate'); // dbOptions 和 options 前面已经提到,这里不再赘述 const dbOptions = { // ... }; const options = {} // ... } // 创建一个 automate 实例 const automate = new Automate(dbOptions, options); (async function main() { // // 获取 Models JSON 定义 // const definitions = await automate.getDefinitions(); // console.log(definitions); // 或生成代码 const code = await automate.run(); console.log(code); })()
Sequelize-Automate 的实现
sequelize-automate 的实现思路很简单,就是首先从数据库中查询到所有表信息,包括表结构、索引、外键等,然后将表信息转换为一个 JSON 定义,最后使用 AST 根据 JSON 定义去生成代码。
获取表信息
查询表信息依赖了 sequelize 的一些方法,这也是为什么 sequelize-automate 依赖了 sequelize,并且有个参数是
{ "user":{ "structures":{ "id":{ "type":"INT(11) UNSIGNED", "allowNull":false, "defaultValue":null, "primaryKey":true, "autoIncrement":true, "comment":"primary ket" }, "name":{ "type":"VARCHAR(100)", "allowNull":false, "defaultValue":null, "primaryKey":false, "autoIncrement":false, "comment":"user name" }, "email":{ "type":"VARCHAR(255)", "allowNull":false, "defaultValue":null, "primaryKey":false, "autoIncrement":false, "comment":"user email" }, "created_at":{ "type":"DATETIME", "allowNull":false, "defaultValue":null, "primaryKey":false, "autoIncrement":false, "comment":"created datetime" }, "updated_at":{ "type":"DATETIME", "allowNull":false, "defaultValue":null, "primaryKey":false, "autoIncrement":false, "comment":"updated datetime" } }, "indexes":[ { "primary":true, "fields":[ { "attribute":"id", "order":"ASC" } ], "name":"PRIMARY", "tableName":"user", "unique":true, "type":"BTREE" }, { "primary":false, "fields":[ { "attribute":"name", "order":"ASC" } ], "name":"uk_name", "tableName":"user", "unique":true, "type":"BTREE" } ], "foreignKeys":[ ] }, "user_post":{ "structures":{ "id":{ "type":"INT(11) UNSIGNED", "allowNull":false, "defaultValue":null, "primaryKey":true, "autoIncrement":true, "comment":"primary key" }, "user_id":{ "type":"INT(11) UNSIGNED", "allowNull":false, "defaultValue":null, "primaryKey":false, "autoIncrement":false, "comment":"user id" }, "title":{ "type":"VARCHAR(255)", "allowNull":false, "defaultValue":null, "primaryKey":false, "autoIncrement":false, "comment":"post title" }, "content":{ "type":"TEXT", "allowNull":true, "defaultValue":null, "primaryKey":false, "autoIncrement":false, "comment":"post content" }, "created_at":{ "type":"DATETIME", "allowNull":false, "defaultValue":null, "primaryKey":false, "autoIncrement":false, "comment":"created datetime" }, "updated_at":{ "type":"DATETIME", "allowNull":false, "defaultValue":null, "primaryKey":false, "autoIncrement":false, "comment":"updated datetime" } }, "indexes":[ { "primary":true, "fields":[ { "attribute":"id", "order":"ASC" } ], "name":"PRIMARY", "tableName":"user_post", "unique":true, "type":"BTREE" }, { "primary":false, "fields":[ { "attribute":"user_id", "order":"ASC" } ], "name":"fk_user_id", "tableName":"user_post", "unique":false, "type":"BTREE" } ], "foreignKeys":[ { "constraint_name":"fk_user_id", "constraintName":"fk_user_id", "constraintSchema":"test", "constraintCatalog":"test", "tableName":"user_post", "tableSchema":"test", "tableCatalog":"test", "columnName":"user_id", "referencedTableSchema":"test", "referencedTableCatalog":"test", "referencedTableName":"user", "referencedColumnName":"id" } ] } }
处理 Models JSON 定义
得到表信息后,就需要将表信息转换为 sequelize models 的定义。比如:将
[ { "modelName":"user_model", "modelFileName":"user", "tableName":"user", "attributes":{ "id":{ "type":"DataTypes.INTEGER(11).UNSIGNED", "allowNull":false, "defaultValue":null, "primaryKey":true, "autoIncrement":true, "comment":"primary ket", "field":"id" }, "name":{ "type":"DataTypes.STRING(100)", "allowNull":false, "defaultValue":null, "primaryKey":false, "autoIncrement":false, "comment":"user name", "field":"name", "unique":"uk_name" }, "email":{ "type":"DataTypes.STRING(255)", "allowNull":false, "defaultValue":null, "primaryKey":false, "autoIncrement":false, "comment":"user email", "field":"email" }, "created_at":{ "type":"DataTypes.DATE", "allowNull":false, "defaultValue":null, "primaryKey":false, "autoIncrement":false, "comment":"created datetime", "field":"created_at" }, "updated_at":{ "type":"DataTypes.DATE", "allowNull":false, "defaultValue":null, "primaryKey":false, "autoIncrement":false, "comment":"updated datetime", "field":"updated_at" } }, "indexes":[ ] }, { "modelName":"user_post_model", "modelFileName":"user_post", "tableName":"user_post", "attributes":{ "id":{ "type":"DataTypes.INTEGER(11).UNSIGNED", "allowNull":false, "defaultValue":null, "primaryKey":true, "autoIncrement":true, "comment":"primary key", "field":"id" }, "user_id":{ "type":"DataTypes.INTEGER(11).UNSIGNED", "allowNull":false, "defaultValue":null, "primaryKey":false, "autoIncrement":false, "comment":"user id", "field":"user_id", "references":{ "key":"id", "model":"user_model" } }, "title":{ "type":"DataTypes.STRING(255)", "allowNull":false, "defaultValue":null, "primaryKey":false, "autoIncrement":false, "comment":"post title", "field":"title" }, "content":{ "type":"DataTypes.TEXT", "allowNull":true, "defaultValue":null, "primaryKey":false, "autoIncrement":false, "comment":"post content", "field":"content" }, "created_at":{ "type":"DataTypes.DATE", "allowNull":false, "defaultValue":null, "primaryKey":false, "autoIncrement":false, "comment":"created datetime", "field":"created_at" }, "updated_at":{ "type":"DataTypes.DATE", "allowNull":false, "defaultValue":null, "primaryKey":false, "autoIncrement":false, "comment":"updated datetime", "field":"updated_at" } }, "indexes":[ { "name":"fk_user_id", "unique":false, "type":"BTREE", "fields":[ "user_id" ] } ] } ]
而后续的
const t = require('@babel/types'); const str = t.stringLiteral('Primary key'); // { type: 'StringLiteral', value: 'Primary key' }
生成对象
const obj = t.objectProperty(t.identifier('comment'), t.stringLiteral("Primary key")); /** { type: 'ObjectProperty', key: { type: 'Identifier', name: 'comment' }, value: { type: 'StringLiteral', value: 'Primary' }, computed: false, shorthand: false, decorators: null } */
然后就可以根据 AST 生成代码:
const generator = require('@babel/generator').default; const code = generate(obj); // { code: 'comment: "Primary"', map: null, rawMappings: null }
需要注意的是,如果要支持中文,则需要设置
const obj = t.objectProperty(t.identifier('comment'), t.stringLiteral("主键")); const code1 = generate(obj); { code: 'comment: "\\u4E3B\\u952E"', map: null, rawMappings: null } const code2 = generate(obj, { jsescOption: { minimal: true, }, }); // { code: 'comment: "主键"', map: null, rawMappings: null }
总结
最开始写 sequelize-automate 是因为每次表结构修改了,都需要手动在代码里面修改 models ,修改起来非常繁琐而且容易写错,当表非常多的时候,写起来就更麻烦了。所以开发了这个小工具,能够让工具做的事情,就尽量让工具去做。
在写 sequelize-automate 之前,其实我也发现了
sequelize/sequelize-auto
也可以用来自动生成 models,但这个包已经几年没有更新了,使用的 sequelize 还是 3.30 版本,现在 sequelize 已经更新到 6.0 了;并且它还有很多 BUG 没有修复,很难使用起来。我也去看了它的代码,感觉很混乱,全是回调嵌套,难以维护。其生成代码也是用的字符串拼接的方式,没有 AST 先进、高端、准确且可预测。所以,毫不犹豫的选择并使用 sequelize-automate 吧!今天关于《Sequelize-Automate: 自动生成 Sequelize Models》的内容就介绍到这里了,是不是学起来一目了然!想要了解更多关于mysql的内容请关注golang学习网公众号!
-
499 收藏
-
244 收藏
-
235 收藏
-
157 收藏
-
101 收藏
-
165 收藏
-
397 收藏
-
489 收藏
-
209 收藏
-
497 收藏
-
335 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 542次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 507次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 497次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 484次学习