代码生成器原理及实现-Python3版

前言

作为开发人员,或多或少都会接触过代码生成器,而亲自写过代码生成器的,可能就不会不少。本文以mysql为例,py为开发语言,讲解代码生成器原理及实现。java

定义

代码生成器:经过读取数据库元数据,而后定制模板,使用模板引擎输出自定义页面或文件的工具node

关于数据库元数据

元数据,是指定义数据结构的数据。那么数据库元数据就是指定义数据库各种对象结构的数据 。下面以mysql为例,说明获取元数据的方式。python

查询数据库名称为db_name的全部表

SELECT t.table_catalog,t.table_schema,t.table_name,table_type FROM information_schema.TABLES t where t.table_schema='db_name'
复制代码

查询数据库名称为db_name,表名为tb_name的表

SELECT t.table_catalog,t.table_schema,t.table_name,table_type FROM information_schema.TABLES t where t.table_schema='db_name' and t.table_name = 'tb_name'
复制代码

查询数据库名称为db_name,以sys_开头的表

SELECT t.table_catalog,t.table_schema,t.table_name,table_type FROM information_schema.TABLES t where t.table_schema='db_name' and t.table_name like 'sys_%'
复制代码

经常使用字段说明mysql

字段 说明
table_catalog 数据表登记目录
table_schema 数据表所属的数据库名
table_name 表名称
table_type 表类型

获取表注释

show table status where NAME='tb_name'
复制代码

经常使用字段说明git

字段 说明
name 表名称
comment 注释

获取表主键

select k.column_name from information_schema.table_constraints t 
join information_schema.key_column_usage k
using (constraint_name,table_schema,table_name) 
where t.constraint_type='PRIMARY KEY' 
and t.table_schema='db_name' and t.table_name='tb_name'
复制代码

获取某个表的全部列

SELECT t.table_schema,t.table_name,t.column_name,t.column_default,
t.is_nullable,t.data_type,t.character_maximum_length,t.numeric_precision,
t.numeric_scale,t.column_type,t.column_key, t.column_comment 
FROM information_schema.columns t
WHERE t.table_schema = 'db_name' AND t.table_name = 'tb_name'
复制代码

经常使用字段说明sql

字段 说明
table_schema 所属的数据库名
table_name 表名称
column_name 列名
column_default 默认值
is_nullable 是否为空(YES->是,NO->否)
data_type 数据类型(char/varchar/int/datetime等无长度说明的定义)
character_maximum_length 字符串类型定义的长度
numeric_precision 数值类型整型部分
numeric_scale 数值类型小数点部分
column_type 相似这种char(36)/int(6)有长度的定义
column_key 约束条件(PRI->主键约束,UNI->惟一约束,MUL能够重复)
column_comment 字段注释

元数据转换

元数据转换即将元数据转换为更为清晰的数据结构,以方便模板引擎。下面使用json结构描述转换后的数据结构(单表样例)。shell

{
    "tableName": "tb_name", // 表名称
    "tableCameName": "tbName", // 表小驼峰名(由表名称转换)
    "tableType": "TABLE", // 表类型	
    "tableAlias":"xxx", // 表别名(ui层输入)
    "remark": "表注释", // 表注释
    "entityName": "TbName", // 实体类名称(由表名称转换)
    "className": "TbName", // java类名称(由表名称转换)
    "schema": "db_name", // 表所属数据库
    "primaryKeys": [{ // 主键列
        "columnName": "id", // 列名
        "remark": "主键" // 字段注释
        ... // 以下同columns
    }],
    "columns": [{ // 表全部列
        "tableName":"tb_name", // 表名
        "columnName": "id", // 列名
        "propertyName":"id", // 属性名
        "primaryKey": true, // 是否为主键
        "foreignKey": false, // 是否为外键(保留,还没有实现)
        "size": 10, // 对应长度
        "decimalDigits": 0 , // 小数点部分
        "nullable": false, // 是否为空
        "autoincrement": false, // 是否默认自增(保留,还没有实现)
        "defaultValue": '', // 默认值
        "remark": "主键",
        "javaType": "String", // java对应数据类型(保留,py不实现)
        "fullJavaType": "java.lang.String", // 同上
        "dataType":"varchar", // 字段数据类型
        "bigDecimal": false, // 同上
        "setterMethodName": "setId", /// java set方法
        "getterMethodName": "getId", // java get 方法
        "date": false, // 是否日期类型
        "treeProperty": false // 是否为id/parent_id模型(树)
    },{
        "columnName": "name", // 列名
        "propertyName":"name", // 属性名
        "primaryKey": false, // 是否为主键
        "foreignKey": false, // 是否为外键(保留,还没有实现)
        "size": 64, // 对应长度
        "decimalDigits": 0 , // 小数点部分
        "nullable": false, // 是否为空
        "autoincrement": false, // 是否默认自增
        "defaultValue": '', // 默认值
        "remark": "名称",
        "javaType": "String", // java对应数据类型(保留,py不实现)
        "fullJavaType": "java.lang.String", // 同上
        "dataType":"varchar", // 字段数据类型
        "bigDecimal": false, // 同上
        "setterMethodName": "setId", /// java set方法 (由列名转换)
        "getterMethodName": "getId", // java get 方法 (由列名转换)
        "date": false, // 是否日期类型
        "treeProperty": false // 是否为id/parent_id模型(树)
    }]
}
复制代码

关于模板引擎

  • java能够考虑:Freemarker、Thymeleaf、Velocity数据库

  • nodejs能够考虑:ejs、art-templatejson

  • python能够考虑:Jinja2bash

关于配置化文件

样例配置,使用yaml

database:
 driverClass: "driverClass" # jdbc驱动
 url: "url" # 数据库地址
 dbName: "dbName" # 数据库名称
 username: "username" # 用户名
 password: "password" # 密码
basePackage: "com.xxx.modules" # 包名
targetProject: "E:\\mldong\\test\\" # 生成代码目标目录
moduleName: "sys" # 模块名
tables: 
 - tableName: "tb_name" # 表名
 tableAlias: "TbName" # 表别名
 remark: "表注释" # 表注释
 - tableName: "sys_%" # 以sys_开头
templates:
 - name: "实体类" # 模板名称
 selected: True # 是否选中,选中则会生成对应代码
 covered: True # 文件存在是否覆盖
 templateFile: "entity.ftl" # 模板文件名称
 targetPath: "{{basePackage}}\\{{moduleName}}\\entity\\" # 代码生成目录
 targetFileName: "{table.className}}.java" # 生成文件名(同上须要占位符,代码中要转换)
 encoding: "utf-8" # 生成文件编码
 - name: "持久层类" # 模板名称
 selected: True # 是否选中,选中则会生成对应代码
 covered: True # 文件存在是否覆盖
 templateFile: "mapper.ftl" # 模板文件名称
 targetPath: "{{basePackage}\\{{moduleName}}\\mapper\\" # 代码生成目录
 targetFileName: "{{table.className}}.java" # 生成文件名(同上须要占位符,代码中要转换)
 encoding: "utf-8" # 生成文件编码
    
复制代码

处理流程

  • 第一步:加载配置文件
  • 第二步:读取数据库元数据
  • 第三步:元数据转换
  • 第四步:组装模板数据
  • 第五步:使用模板引擎生成代码

注意:为了方便模板引擎使用模板数据,从新组装了数据结构以下:

{
    ...config/app.yml // 配置文件数据,
    table: {
    	"tableName": "tb_name", // 表名称
    	"tableCameName": "tbName", // 表小驼峰名(由表名称转换)
    	... 其余元数据转换后的数据
	}
}
复制代码

开始编码

目录结构

├── config
	└── app.yml      # 配置文件
├── templates        # 模板目录
	├── entity.ftl   # 实体类模板
	└── mapper.ftl   # 持久层模板
├── main.py          # py脚本
└── requirements.txt # 依赖库
复制代码

main.py

执行命令

python main.py
复制代码

经常使用命令说明

  1. 安装指定库
pip3 install requests -i http://mirrors.aliyun.com/pypi/simple/ --trusted-host mirrors.aliyun.com
复制代码
  1. 生成依赖
pip3 freeze  > requirements.txt 
复制代码
  1. 安装依赖
pip install -r requirements.txt -i http://mirrors.aliyun.com/pypi/simple/ --trusted-host mirrors.aliyun.com
复制代码

源码地址

相关文章
相关标签/搜索