基于Vue和Quasar的前端SPA我的项目实战之数据库逆向(十二)

回顾

通过之前文章 基于Vue和Quasar的前端SPA我的项目实战之动静表单(五)的介绍,实现了动静表单性能。如果是全新的我的项目,通过配置元数据并且创立物理表,从而主动实现业务数据的CRUD增删改查。然而如果数据库表曾经存在的状况下,如何通过配置表单元数据进行治理呢?这时候数据库逆向性能就很有必要了。

简介

数据库逆向就是通过读取数据库物理表schema信息,而后生成表单元数据,能够看成“dbfirst”模式,即先有数据库表,而后依据表生成元数据,逆向表单后续操作和一般动静表单相似。

UI界面

输出物理表名称,启用“数据库逆向”性能,而后点击“加载元数据”,而后会主动填充表单字段相干元数据信息。

数据表筹备

以ca_product产品为例,通过phpmyadmin创立表

创立产品表

CREATE TABLE `ca_product` (  `id` bigint UNSIGNED NOT NULL COMMENT '编号',  `name` varchar(200) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '名称',  `fullTextBody` text COLLATE utf8mb4_unicode_ci COMMENT '全文索引',  `createdDate` datetime NOT NULL COMMENT '创立工夫',  `lastModifiedDate` datetime DEFAULT NULL COMMENT '批改工夫',  `code` varchar(200) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '编码',  `brand` varchar(200) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '品牌',  `price` decimal(10,0) DEFAULT NULL COMMENT '单价',  `weight` decimal(10,0) DEFAULT NULL COMMENT '分量',  `length` decimal(10,0) DEFAULT NULL COMMENT '长',  `width` decimal(10,0) DEFAULT NULL COMMENT '宽',  `high` decimal(10,0) DEFAULT NULL COMMENT '高',  `ats` bigint DEFAULT NULL COMMENT '库存个数') ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='产品';ALTER TABLE `ca_product`  ADD PRIMARY KEY (`id`),  ADD UNIQUE KEY `UQ_CODE` (`code`) USING BTREE;ALTER TABLE `ca_product` ADD FULLTEXT KEY `ft_fulltext_body` (`fullTextBody`);ALTER TABLE `ca_product`  MODIFY `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '编号', AUTO_INCREMENT=1;COMMIT;

查问schema

mysql数据库通过如下SQL语句能够查问表单、字段、索引等信息

SHOW TABLE STATUS LIKE TABLE_NAMESHOW FULL COLUMNS FROM TABLE_NAMESHOW INDEX FROM TABLE_NAME


表根本信息


字段信息


索引信息

API JSON

通过APIhttps://demo.crudapi.cn/api/metadata/tables/metadata/ca_product
查问ca_product的schema信息, 格局如下:

{  "Name": "ca_product",  "Engine": "InnoDB",  "Version": 10,  "Row_format": "Dynamic",  "Rows": 0,  "Avg_row_length": 0,  "Data_length": 16384,  "Max_data_length": 0,  "Index_length": 32768,  "Data_free": 0,  "Auto_increment": 2,  "Create_time": 1628141282000,  "Update_time": 1628141304000,  "Collation": "utf8mb4_unicode_ci",  "Create_options": "",  "Comment": "产品",  "columns": [{    "Field": "id",    "Type": "bigint unsigned",    "Null": "NO",    "Key": "PRI",    "Extra": "auto_increment",    "Privileges": "select,insert,update,references",    "Comment": "编号"  }, {    "Field": "name",    "Type": "varchar(200)",    "Collation": "utf8mb4_unicode_ci",    "Null": "NO",    "Key": "",    "Extra": "",    "Privileges": "select,insert,update,references",    "Comment": "名称"  }, {    "Field": "fullTextBody",    "Type": "text",    "Collation": "utf8mb4_unicode_ci",    "Null": "YES",    "Key": "MUL",    "Extra": "",    "Privileges": "select,insert,update,references",    "Comment": "全文索引"  }, {    "Field": "createdDate",    "Type": "datetime",    "Null": "NO",    "Key": "",    "Extra": "",    "Privileges": "select,insert,update,references",    "Comment": "创立工夫"  }, {    "Field": "lastModifiedDate",    "Type": "datetime",    "Null": "YES",    "Key": "",    "Extra": "",    "Privileges": "select,insert,update,references",    "Comment": "批改工夫"  }, {    "Field": "code",    "Type": "varchar(200)",    "Collation": "utf8mb4_unicode_ci",    "Null": "YES",    "Key": "UNI",    "Extra": "",    "Privileges": "select,insert,update,references",    "Comment": "编码"  }, {    "Field": "brand",    "Type": "varchar(200)",    "Collation": "utf8mb4_unicode_ci",    "Null": "YES",    "Key": "",    "Extra": "",    "Privileges": "select,insert,update,references",    "Comment": "品牌"  }, {    "Field": "price",    "Type": "decimal(10,0)",    "Null": "YES",    "Key": "",    "Extra": "",    "Privileges": "select,insert,update,references",    "Comment": "单价"  }, {    "Field": "weight",    "Type": "decimal(10,0)",    "Null": "YES",    "Key": "",    "Extra": "",    "Privileges": "select,insert,update,references",    "Comment": "分量"  }, {    "Field": "length",    "Type": "decimal(10,0)",    "Null": "YES",    "Key": "",    "Extra": "",    "Privileges": "select,insert,update,references",    "Comment": "长"  }, {    "Field": "width",    "Type": "decimal(10,0)",    "Null": "YES",    "Key": "",    "Extra": "",    "Privileges": "select,insert,update,references",    "Comment": "宽"  }, {    "Field": "high",    "Type": "decimal(10,0)",    "Null": "YES",    "Key": "",    "Extra": "",    "Privileges": "select,insert,update,references",    "Comment": "高"  }, {    "Field": "ats",    "Type": "bigint",    "Null": "YES",    "Key": "",    "Extra": "",    "Privileges": "select,insert,update,references",    "Comment": "库存个数"  }],  "indexs": [{    "Table": "ca_product",    "Non_unique": 0,    "Key_name": "PRIMARY",    "Seq_in_index": 1,    "Column_name": "id",    "Collation": "A",    "Cardinality": 0,    "Null": "",    "Index_type": "BTREE",    "Comment": "",    "Index_comment": "",    "Visible": "YES"  }, {    "Table": "ca_product",    "Non_unique": 0,    "Key_name": "UQ_CODE",    "Seq_in_index": 1,    "Column_name": "code",    "Collation": "A",    "Cardinality": 0,    "Null": "YES",    "Index_type": "BTREE",    "Comment": "",    "Index_comment": "",    "Visible": "YES"  }, {    "Table": "ca_product",    "Non_unique": 1,    "Key_name": "ft_fulltext_body",    "Seq_in_index": 1,    "Column_name": "fullTextBody",    "Cardinality": 0,    "Null": "YES",    "Index_type": "FULLTEXT",    "Comment": "",    "Index_comment": "",    "Visible": "YES"  }]}

外围代码

前端依据API返回的schema信息,转换成crudapi的元数据格式,并显示在UI上, 次要代码在文件metadata/table/new.vue中,通过addRowFromMetadata办法增加字段,addIndexFromMetadata增加联结索引。

addRowFromMetadata(id, t, singleIndexColumns) {  const columns = this.table.columns;  const index = columns.length + 1;  const type = t.Type.toUpperCase();  const name = t.Field;  let length = null;  let precision = null;  let scale = null;  let typeArr = type.split("(");  if (typeArr.length > 1) {    const lengthOrprecisionScale = typeArr[1].split(")")[0];    if (lengthOrprecisionScale.indexOf(",") > 0) {      precision = lengthOrprecisionScale.split(",")[0];      scale = lengthOrprecisionScale.split(",")[1];    } else {      length = lengthOrprecisionScale;    }  }  let indexType = null;  let indexStorage = null;  let indexName = null;  let indexColumn = singleIndexColumns[name];  if (indexColumn) {    if (indexColumn.Key_name === "PRIMARY") {      indexType = "PRIMARY";    } else if (indexColumn.Index_type === "FULLTEXT") {      indexType = "FULLTEXT";      indexName = indexColumn.Key_name;    } else if (indexColumn.Non_unique === 0) {      indexType = "UNIQUE";      indexName = indexColumn.Key_name;      indexStorage = indexColumn.Index_type;    } else {      indexType = "INDEX";      indexName = indexColumn.Key_name;      indexStorage = indexColumn.Index_type;    }  }  const comment = t.Comment ? t.Comment : name;  const newRow = {    id: id,    autoIncrement:  (t.Extra === "auto_increment"),    displayOrder: columns.length,    insertable: true,    nullable: (t.Null === "YES"),    queryable: true,    displayable: false,    unsigned: type.indexOf("UNSIGNED") >= 0,    updatable: true,    dataType : typeArr[0].replace("UNSIGNED", "").trim(),    indexType: indexType,    indexStorage: indexStorage,    indexName: indexName,    name: name,    caption: comment,    description: comment,    length: length,    precision: precision,    scale: scale,    systemable: false  };  this.table.columns  = [ ...columns.slice(0, index), newRow, ...columns.slice(index) ];},addIndexFromMetadata(union) {  let baseId = (new Date()).valueOf();  let newIndexs = [];  const tableColumns = this.table.columns;  console.dir(tableColumns);  for (let key in union) {    const unionLines = union[key];    const newIndexLines = [];    unionLines.forEach((item) => {      const columnName = item.Column_name;      const columnId = tableColumns.find(t => t.name === columnName).id;      newIndexLines.push({        column: {          id: columnId,          name: columnName        }      });    });    const unionLineFirst = unionLines[0];    let indexType = null;    let indexStorage = null;    if (unionLineFirst.Key_name === "PRIMARY") {      indexType = "PRIMARY";    } else if (unionLineFirst.Non_unique === 0) {      indexType = "UNIQUE";      indexStorage = unionLineFirst.Index_type;    } else {      indexType = "INDEX";      indexStorage = unionLineFirst.Index_type;    }    const indexComment = unionLineFirst.Index_comment ? unionLineFirst.Index_comment:  unionLineFirst.Key_name;    const newIndex = {      id: baseId++,      isNewRow: true,      caption: indexComment,      description: indexComment,      indexStorage: indexStorage,      indexType: indexType,      name: unionLineFirst.Key_name,      indexLines: newIndexLines    }    newIndexs.push(newIndex);  }  this.table.indexs = newIndexs;  if (this.table.indexs) {    this.indexCount = this.table.indexs.length;  } else {    this.indexCount = 0;  }}

例子

以ca_product为例子, 点击“加载元数据之后”,表字段和索引都正确地显示了。保留胜利之后,曾经存在的物理表ca_product会主动被元数据管理起来,后续能够通过crudapi后盾持续编辑,通过数据库逆向性能,零代码实现了物理表ca_product的CRUD增删改查性能。

小结

本文次要介绍了数据库逆向性能,在数据库表单曾经存在的根底上,通过数据库逆向性能,疾速生成元数据,不须要一行代码,咱们就能够失去已有数据库的根本crud性能,包含API和UI。相似于phpmyadmin等数据库UI管理系统,然而比数据库UI管理系统更灵便,更敌对。目前数据库逆向一次只反对一个表,如果同时存在很多物理表,就须要批量操作了。后续会持续优化,实现批量数据库逆向性能。

demo演示

官网地址:https://crudapi.cn
测试地址:https://demo.crudapi.cn/crudapi/login

附源码地址

GitHub地址

https://github.com/crudapi/crudapi-admin-web

Gitee地址

https://gitee.com/crudapi/crudapi-admin-web

因为网络起因,GitHub可能速度慢,改成拜访Gitee即可,代码同步更新。