编写一个工具, 从 DDL SQL 自动生成 POJOs

Python 让繁琐工作自动化

编写一个工具, 从 DDL SQL 自动生成 POJOs_第1张图片

原因每次写完 DDL SQL 再手动对应的 POJOs,太繁琐了,为什么不写个脚本自动化下呢(还可以顺带检测下自己算法能力),说干就干,这就来了。

WARNING ⚠:别学我,IDEA 有 DDL 一键生成 POJOs 插件,我丫的是 VSCode 党 o( ̄┰ ̄*)ゞ

算法步骤

  1. 建立一个 POJO 字符串模板,可以直接填充包名导入语句类名字段语句
  2. 解析 DDLSQL 语句,得到类名(table name)、字段名类型(column name and type)
  3. 从 2 中的得到的字段名和类型,整理出来字段语句导入语句
  4. 包名导入语句类名字段语句填充到 POJO 字符串模板中得到完整的 java class 字符串,return 、print 或 write to file

代码实现 - Python

python,粘贴即使用,真方便

"""
从 DDL SQL 生成 java POJO

- 列名即 java field 名,不做下划线、驼峰命名等转换
- 使用 Lombok 注解替换 setter/getter
"""

from typing import Dict, List
import re

MYSQL_DATATYPE_NUMBER =  {'TINYINT', 'SMALLINT', 'MEDIUMINT', 'INT', 'INTEGER', 'BIGINT', 'FLOAT', 'DOUBLE', 'DECIMAL'}
MYSQL_DATATYPE_DATE = {'DATE', 'TIME', 'DATETIME', 'TIMESTAMP'}
MYSQL_DATATYPE_TEXT = {'CHAR', 'VARCHAR', 'TINYBLOB', 'TINYTEXT', 'BLOB', 'TEXT', 'MEDIUMBLOB', 'MEDIUMTEXT', 'LONGBLOB', 'LONGTEXT'}

POJO_TEMPLAATE = """package {package};

{imports}
import lombok.Data;

@Data
public class {name} {{
    
    {fields}
}}"""

def __generate_pojo_text(package_name: str, class_name: str, columns: Dict[str,str]) -> str:
    """
    生成 pojo 文本
    """

    field_rows = []
    import_rows = set()
    field_template = "private {} {};"
    for name, type in columns.items():
        field_class = __sql_java_type_mapping(type)
        if field_class == "BigDecimal":
            import_rows.add("import java.math.BigDecimal")
        elif "Local" in field_class:
            import_rows.add("import java.time.{};".format(field_class))
        field_row = field_template.format(field_class, name)
        field_rows.append(field_row)
    pojo_fill_data = {
        'package': package_name,
        'name': class_name.title(),
        'imports': "\n".join(import_rows),
        'fields': "\n    ".join(field_rows)
    }

    return POJO_TEMPLAATE.format_map(pojo_fill_data)


def __sql_java_type_mapping(sql_type: str) -> str:
    """
    sql java 数据类型映射
    """
    if sql_type in MYSQL_DATATYPE_TEXT:
        return "String"
    if sql_type in MYSQL_DATATYPE_NUMBER:
        MAPPING = {
            "BIGINT": "String",
            "FLOAT": "Float",
            "DOUBLE": "Double",
            "DECIMAL": "BigDecimal"
        }
        return MAPPING.get(sql_type, "Integer")
    if sql_type in MYSQL_DATATYPE_DATE:
        MAPPING = {
            "DATE": "LocalDate",
            "TIME": "LocalTime",
            "DATETIME": "LocalDateTime",
            "TIMESTAMP": "LocalDateTime"
        }
        return MAPPING.get(sql_type, "Object")
    return "Object"

def __parse_ddl(sql: str) -> Dict[str, Dict[str, str]]:
    """
    解析 ddl sql
    """

    def find_brackets_content(text: str) -> str:
        """
        找到 (...) 里的内容
        """
        left_brackets = 0
        start, end = 0,0
        for i, e in enumerate(text):
            if e == '(':
                left_brackets += 1
                if start == 0: start = i
            elif e == ')':
                left_brackets -= 1
                if left_brackets == 0:
                    end = i
                    break
        return text[start+1:end].strip()

    table_blocks = re.split("create table", sql, flags=re.I)[1:]
    tables = {} # name: column
    for block in table_blocks:
        table_name = block.split()[0]
        columns = {} # name: type
        columns_block = find_brackets_content(block)
        for column_block in columns_block.split(','):
            # column_block = column_block.strip()
            words = column_block.split()
            if not words or len(words) < 2: continue
            type = words[1] if '(' not in words[1] else words[1][:words[1].index('(')]
            type = type.upper()
            if type in MYSQL_DATATYPE_DATE or type in MYSQL_DATATYPE_NUMBER or type in MYSQL_DATATYPE_TEXT:
                columns[words[0]] = type
        tables[table_name] = columns
    return tables
        

def __write_file(path, context):
    with open(path, "w", encoding="UTF-8") as f:
        f.write(context)


def generate_pojo(sql: str, package_name: str, target: str='./') ->List[str]:
    """
    从 ddl sql 生成对应的 java pojo 类文件

    Args:
        - sql: ddl sql 字符串或 sql文件路径(必须以.sql为后缀)
        - package_name: 生成的 POJO 所在的包名
        - target: POJO的生成路径,默认是当前路径

    Return:
        List[str]: 表示 pojos
    """
    
    if sql.endswith('.sql'):
        with open(sql, 'r', encoding='utf-8') as f:
            sql = f.read()
    tables =  __parse_ddl(sql)
    pojos = []
    pojos_name = []
    for table_name, columns in tables.items():
        pojo = __generate_pojo_text(package_name, table_name, columns)
        pojos.append(pojo)
        pojos_name.append(table_name.title())

    import os
    for pojo, name in zip(pojos, pojos_name):
        path = os.path.join(target, name + '.java')
        with open(path, 'w', encoding='utf-8') as f:
            f.write(pojo)
        print("[info] - class {}.{} is generated".format(package_name, name))

    print("[info] - success, {} classes were generated".format(len(pojos)))
    return pojos


TEST_SQL = """DROP TABLE IF EXISTS book
CREATE TABLE book (
    id int AUTO_INCREMENT PRIMARY KEY,
    title varchar(100) NOT NULL,
    author varchar(50) NOT NULL,
    translator varchar(50) NULL,
    publisher varchar(50) NOT NULL,
    publish_date date NOT NULL,
    price int NOT NULL,
    isbn varchar(50) NOT NULL
)
"""

# 测试
def test():
    package_name = "com.onemsg.pojo.test"
    sql_path = 'db/schema.sql'
    target = 'db'
    pojos = generate_pojo(sql_path, package_name=package_name, target=target)

if __name__ == "__main__":

    test()

测试

  • db/schema.sql

DROP TABLE IF EXISTS book
CREATE TABLE book (
    id int AUTO_INCREMENT PRIMARY KEY,
    title varchar(100) NOT NULL,
    author varchar(50) NOT NULL,
    translator varchar(50) NULL,
    publisher varchar(50) NOT NULL,
    publish_date date NOT NULL,
    price int NOT NULL,
    isbn varchar(50) NOT NULL
)

DROP TABLE IF EXISTS person
CREATE TABLE person (
    id int AUTO_INCREMENT PRIMARY KEY,
    name varchar(50) NOT NULL,
    gender tinyint NULL,
    birthday datetime NULL,
    address varchar(100) NULL,
    phone char(13) NULL,
    email varchar(50) NULL
)
  • test code

def test():
    package_name = "com.onemsg.pojo.test"
    sql_path = 'db/schema.sql'
    target = 'db'
    pojos = generate_pojo(sql_path, package_name=package_name, target=target)
  • console log & files

编写一个工具, 从 DDL SQL 自动生成 POJOs_第2张图片
编写一个工具, 从 DDL SQL 自动生成 POJOs_第3张图片
编写一个工具, 从 DDL SQL 自动生成 POJOs_第4张图片

你可能感兴趣的:(Coding,Adventure,python,pojos自动化)