一二三应用开发平台——代码生成(2)——生成库表

生成库表

先来说说相对简单的生成数据库表的功能实现。

@Override
public void generateTable(String entityCode) {
    // 获取实体配置信息
    Entity entity = entityService.getByCode(entityCode);
    // 获取模块配置信息
    Module module = moduleService.query(entity.getModule());


    // 获取模型列表
    List<EntityModel> entityModelList = entityModelService.getByEntityId(entity.getId());
    // 未找到模型,抛出异常
    if (CollectionUtils.isEmpty(entityModelList)) {
        throw new CustomException(EntityException.MODEL_NOT_FOUND, entity.getName());
    }
    // 遍历列表
    entityModelList.stream().forEach((entityModel) -> {

        // 获取自身标识
        String entityModelId = entityModel.getId();
        // 获取库表名称
        String tableName = generateTableName(entityModel.getCode(), module.getAbbreviation());
        // 获取库表备注
        String tableComment = entityModel.getName();
        // 获取父级模型属性列表
        List<EntityModelProperty> parentModelPropertyList = new ArrayList<>();
        // 循环向上查找父级模型
        while (true) {
            entityModel = entityModelService.query(entityModel.getParentModel());
            // 获取父级模型属性列表
            List<EntityModelProperty> currentPropertyList = entityModelPropertyService.getDatabaseStoreListByEntityModelId(entityModel.getId());
            CollectionUtils.addAll(parentModelPropertyList, currentPropertyList.iterator());

            // 找到顶层节点标识模型停止
            if (entityModel.getId().equals(EntityConfigConstant.ID_MODEL_ID)) {
                break;
            }

        }
        ;

        // 获取实体模型属性列表,非库表存储字段忽略
        List<EntityModelProperty> entityModelPropertyList = entityModelPropertyService.getDatabaseStoreListByEntityModelId(entityModelId);
        CollectionUtils.addAll(entityModelPropertyList, parentModelPropertyList.iterator());
        // 将实体模型属性转换为库表字段信息
        List<TableFieldInfo> tableFieldInfoList = convertModelPropertyData(entityModelPropertyList);

        entityModelService.createTable(tableName, tableComment, tableFieldInfoList);

    });


}

方法中添加了详细的注释,不过与实体配置模块的功能设计密切相关,只看代码不了解背后的设计也难以看清楚,我在这里把重点说一下。

实体配置模块中,实体是一个逻辑概念,一个实体可能包含一个或多个数据模型,而数据模型对应着数据库中的库表。例如,销售订单是一个实体,包括两个数据模型,订单信息和订单明细。一组相关的实体,可以归属到一个模块,模块同样是一个逻辑概念,跟通常说的一个系统由多个功能模块组成中的模块含义是相同的。

在生成库表的时候,我们选择一个或多个实体,点击生成库表操作,平台会根据实体编码进行循环处理。

首先根据实体编码,查询对应的实体配置,以及该实体归属的模块信息。

然后查询该实体下属的所有数据模型,针对数据模型,来生成对应的数据库表。

这里还涉及到一个模型继承与复用的核心设计。

系统预置了四个模型:标识模型(只有标识id属性)、映射模型(继承于标识模型,增加了创建人、创建时间、修改人、修改时间)、业务模型(继承于映射模型,增加了逻辑删除标识位)、流程模型(继承于业务模型,增加了与流程相关的一系列发起人、流程状态等属性)。

通过模型继承,实现了模型的灵活扩展和属性字段复用的目的。

因此在读取实体模型属性配置的时候,需要递归往上找父级模型,只到最顶层标识模型,这样才能完整获取到库表的所有字段。

此外,实体中部分属性标记了非库表存储属性,意味着不需要数据库来生成对应的字段,这部分要排除掉。

接下来,通过convertModelPropertyData方法,将数据模型属性配置,转换为数据库字段,如下:

/**
 * 将实体模型属性列表转换为库表字段信息列表
 *
 * @param entityModelPropertyList 实体模型属性列表
 * @return 库表字段信息列表
 */
private List<TableFieldInfo> convertModelPropertyData(List<EntityModelProperty> entityModelPropertyList) {
    List<TableFieldInfo> tableFieldInfoList = new ArrayList<>();
    entityModelPropertyList.stream().forEach(modelProperty -> {

        TableFieldInfo tableFieldInfo = new TableFieldInfo();
        // 将驼峰字符串转换为下划线格式
        String fieldCode = CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, modelProperty.getCode());
        tableFieldInfo.setCode(fieldCode);
        tableFieldInfo.setName(modelProperty.getName());
        tableFieldInfo.setDataType(generateDatabaseDataType(modelProperty));

        // 是否可为空处理,实际不再使用
        String nullFlag = "";
        if (modelProperty.getNullFlag().equals(YesOrNoEnum.NO.name())) {
            nullFlag = "not null";
        }
        tableFieldInfo.setNullFlag(nullFlag);


        // 库表默认值设定,实际不再使用
        String defaultValue = "";
        if (StringUtils.isNotBlank(modelProperty.getDefaultValue())) {
            defaultValue = "DEFAULT '" + modelProperty.getDefaultValue() + "'";
        }
        tableFieldInfo.setDefaultValue(defaultValue);

        tableFieldInfoList.add(tableFieldInfo);
    });
    return tableFieldInfoList;
}

其中TableFieldInfo类是自己定义的一个实体类,用来描述表字段信息,源码如下:

package tech.abc.platform.entityconfig.codegenerator.entity;

import lombok.Data;

/**
 * 表字段信息
 * @author wqliu
 * @date 2022-11-8
 */
@Data
public class TableFieldInfo {
    /**
     * 编码
     */
    private String code;
    /**
     * 名称
     */
    private String name;
    /**
     * 数据类型
     */
    private String dataType;
    /**
     * 是否可为空 YES 是 NO 否
     */
    private String nullFlag;
    /**
     * 默认值
     */
    private String defaultValue;

}

数据类型使用了一个辅助处理方法,将实体配置中约定的类型字符串,对应到MySql数据库的数据类型上,源码如下:

    /**
     * 生成库表的数据类型
     *
     * @param modelProperty 模型属性
     * @return {@link String}
     */
    private String generateDatabaseDataType(EntityModelProperty modelProperty) {
        String result = StringUtils.EMPTY;
        Integer maxLength = modelProperty.getMaxLength();
        Integer decimalLength = modelProperty.getDecimalLength();
        EntityModelPropertyTypeEnum dataType = EnumUtils.getEnum(EntityModelPropertyTypeEnum.class, modelProperty.getDataType());
        switch (dataType) {
            case STRING:
                result = "VARCHAR(" + maxLength + ")";
                break;
            case INTEGER:
                result = "INT";
                break;
            case LONG:
                result = "BIGINT";
                break;
            case DOUBLE:
                result = "DOUBLE";
                break;
            case DATETIME:
                result = "DATETIME";
                break;
            case DECIMAL:
                result = "DECIMAL(" + maxLength + "," + decimalLength + ")";
                break;
            case TEXT:
                result = "TEXT";
                break;
            case TIME:
                result = "VARCHAR(10)";
                break;
            default:
                result = "VARCHAR(" + maxLength + ")";
        }

        return result;

    }

最后,调用实体数据模型服务的创建表的方法,来生成数据库表,如下:

public void createTable(String tableName, String tableComment, List tableFieldInfoList) {
    this.baseMapper.createTable(tableName, tableComment, tableFieldInfoList);
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="tech.abc.platform.entityconfig.mapper.EntityModelMapper">

    <!--创建表-->
    <update id="createTable" statementType="STATEMENT">
        -- TODO 测试方便,每次生成前都删除库表,正式启用后去除删除,避免数据丢失
        DROP TABLE IF EXISTS `${tableName}`;
        CREATE TABLE `${tableName}`
        (
        <foreach collection="tableFieldInfoList" separator="," item="item">
            `${item.code}` ${item.dataType} COMMENT '${item.name}'
        </foreach>
        ,PRIMARY KEY (`id`)
        )
        ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT = '${tableComment}';
    </update>

</mapper>

注意,这里最开始的时候还有是否为空和默认值的处理,后来去除了,将相关控制放在程序的逻辑层里处理,不在数据库层面进行处理。

开发平台资料

平台名称:一二三应用开发平台
简介: 企业级通用低代码应用开发平台,免费全开源可商用
设计资料:csdn专栏
开源地址:Gitee
开源协议:MIT

如果您在阅读本文时获得了帮助或受到了启发,我衷心希望您能够喜欢并收藏这篇文章,为它点赞,并在评论区与我分享您的想法和心得。让我们一起交流学习,不断进步,遇见更加优秀的自己!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

行者无疆1982

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值