Foxnic-Web 代码生成 (1) —— 开始生成代码

Foxnic-Web 代码生成 (1) —— 开始生成代码

基本原理

使用 Foxnic-Web 以及 Foxnic-SQL 进行应用开发时,都可以支持代码生成。他们的区别是,基于 Foxnic-SQL 的快速 main 函数启动的应用,只需要生成 Model 和 Service 即可。基于 Foxnic-Web 开发 Web 应用时,除了生成 Model 和 Service 以外,还要生成 Proxy、Controller、UI界面等。

Foxnic 的代码生成是基于数据表的,所以当表结构变更,甚至只是注释的调整,我们也是建议重新生成必要的代码。在 Foxnic 的体系中,我们认为最初的表结构设计、ER图设计,就是这个系统设计的起点。后续的程序设计或数据结构设计都是表结构设计的延续。

Foxnic 的代码生成体系希望开发者可以有一个较高的开发起点,可以基于生成的代码直接开发应用,甚至是代码生成后无需修改就可以直接使用了。另一方面,我们又不关闭二次开发的开放性,毕竟自由的修改代码才是软件系统可以按需定制的终极路径。这也是 Foxnic 体系没有走无代码或低代码平台的原因。

本文中的示例代码均可在 foxnic-samples: 用于展示和提供 Foxnic 相关的示例工程和代码 项目中找到。

生成表结构元数据

代码生成的第一步是生成表结构元数据。我们知道,数据表、列、索引等信息都存储在数据库内,我们在 java 程序内无法直接引用。但有些时候,我们又需要用到这些东西,特别是表名、字段等。Foxnic-Web 在代码生成、以及其它业务逻辑编写时都有可能用到这些元数据。

所以,我们要通过一种方式将数据库元数据转换成 java 结构,这种结构是通过代码生成的。Java 生成的元数据它是静态的,不会随着变结构的改变而改变,Foxnic-Web 要求表结构变动后,需要重新生成元数据类。

示例项目的 webfull 项目下的 WebFullDBMetaGenerator 类用于生成数据库元数据类:

package org.github.foxnic.web.generator.constants;

import com.github.foxnic.dao.spec.DAO;
import com.github.foxnic.generator.builder.constants.DBMetaClassFile;
import org.github.foxnic.web.generator.config.WebFullConfigs;

public class WebFullDBMetaGenerator {
    /**
    * 运行main函数生成代码
    * */
    public static void main(String[] args) throws Exception {
        WebFullDBMetaGenerator g = new WebFullDBMetaGenerator();
        g.buildDBMeta();
    }
    /**
    * 生成DBMeta数据
    * */
    private void buildDBMeta() {
        WebFullConfigs configs=new WebFullConfigs("webfull-service-example");
        DAO dao=configs.getDAO();
        DBMetaClassFile dbMetaBuilder=new DBMetaClassFile(dao,configs.getDomainProject(),configs.getProjectConfigs().getDomainConstantsPackage(),"WebFullTables");
        dbMetaBuilder.setTableFilter(table->{
            table=table.toLowerCase();
            // 仅生成以 example_ 开头的表
            if(table.startsWith("webfull_")) return true;
            return false;
        });
        dbMetaBuilder.save(true);
    }
}

执行 main 函数后,在 domain 模块内生成 WebFullTables 类,后面生成其它代码,我们会用到这个类中数据库表结构定义的常量。

非 Web 环境的代码生成

事实上,非 Web 环境的开发是很少的,但是我们在讲解 Foxnic-SQL 部分的时候,也用到了非 Web 环境的代码生成,大家参考 Foxnic-SQL 相关的文档即可。

foxnic-samples: 用于展示和提供 Foxnic 相关的示例工程和代码 项目的 com/leefj/foxnic/sql/demo/generator 目录的

ExampleCodeGenerator.java 中有示例,大家看代码结合我们的文档,很容易理解。后面的篇幅我们着重介绍 Web 环境下的代码生成,所有 Web 环境下代码生成的原理一样适用于非 Web 环境。

为了方便理解,我们还是贴一下代码:

package com.leefj.foxnic.sql.demo.generator;

import com.github.foxnic.commons.project.maven.MavenProject;
import com.github.foxnic.dao.spec.DAO;
import com.github.foxnic.generator.builder.model.PoClassFile;
import com.github.foxnic.generator.config.ModuleContext;
import com.github.foxnic.sql.meta.DBTable;
import com.leefj.foxnic.sql.demo.app.domain.example.Address;
import com.leefj.foxnic.sql.demo.app.domain.example.Goods;
import com.leefj.foxnic.sql.demo.app.domain.example.Order;
import com.leefj.foxnic.sql.demo.app.domain.example.OrderItem;
import com.leefj.foxnic.sql.demo.config.DBInstance;
import com.leefj.foxnic.sql.demo.config.db.ExampleTables;

/**
* 代码生成器
* */
public class ExampleCodeGenerator {
    public static interface  Config  {
        void config(PoClassFile poType);
    }

    private static final String BASE_PACKAGE = "com.leefj.foxnic.sql.demo.app";

    /**
    * 需要首先运行 ExampleDBMetaGenerator 生成 ExampleTables 类
    * */
    public static void main(String[] args) {
        ExampleCodeGenerator generator = new ExampleCodeGenerator();
        // 生成商品实体类
        generator.generate(ExampleTables.EXAMPLE_GOODS.$TABLE, poType -> {
            // Goods 对象 通过 orderList 属性持有 Order
            poType.addListProperty(Goods.class,"orderList","订单明细商品","订单明细商品");
            // Goods 对象 通过 addressList 属性持有 Address
            poType.addListProperty(Address.class,"addressList","收件地址","收件地址,包括收件人以及手机号码");
            // Goods 对象 通过 itemList 属性持有 OrderItem
            poType.addListProperty(OrderItem.class,"itemList","订单明细","订单明细");
        });
        // 生成订单实体类
        generator.generate(ExampleTables.EXAMPLE_ORDER.$TABLE , poType -> {
            // Order 对象 通过 goodsList 属性持有 Goods
            poType.addListProperty(Goods.class,"goodsList","订单明细商品","订单明细商品");
            // Order 对象 通过 address 属性持有 Address
            poType.addSimpleProperty(Address.class,"address","收件地址","收件地址,包括收件人以及手机号码");
            // Order 对象 通过 itemList 属性持有 OrderItem
            poType.addListProperty(OrderItem.class,"itemList","订单明细","订单明细");
        });
        // 生成订单明细实体类
        generator.generate(ExampleTables.EXAMPLE_ORDER_ITEM.$TABLE, poType -> {
            // OrderItem 对象 通过 goodsList 属性持有 Goods
            poType.addSimpleProperty(Goods.class,"goods","订单明细商品","订单明细商品");
            // OrderItem 对象 通过 address 属性持有 Address
            poType.addSimpleProperty(Address.class,"address","收件地址","收件地址,包括收件人以及手机号码");
            // OrderItem 对象 通过 order 属性持有 Order
            poType.addListProperty(Order.class,"order","订单","订单");
        });
        // 生成地址实体类
        generator.generate(ExampleTables.EXAMPLE_ADDRESS.$TABLE, poType -> {
            // Address 对象 通过 goodsList属性 持有 Goods
            poType.addListProperty(Goods.class,"goodsList","订单明细商品","订单明细商品");
            // Address 对象 通过 orderList 持有 Order
            poType.addListProperty(Address.class,"orderList","收件地址","收件地址,包括收件人以及手机号码");
            // Address 对象 通过 itemList 持有 OrderItem
            poType.addListProperty(OrderItem.class,"itemList","订单明细","订单明细");
		});
	}
    /**
    * 按表生成
    * */
    public void generate(DBTable table) {
    	generate(table,null);
    }
    /**
    * 按表生成
    * */
    public void generate(DBTable table,Config config) {
        DAO dao = DBInstance.DEFAULT.dao();
        MavenProject project = GeneratorUtil.getProject();
        String pkg = table.name().split("_")[0];
        String prefix = pkg + "_";
        ModuleContext context = new ModuleContext(GeneratorUtil.initGlobalSettings(),table,prefix,BASE_PACKAGE + "." + pkg);
        context.setDomainProject(project);
        context.setServiceProject(project);
        context.setDAO(dao);
        if(config!=null) {
        	config.config(context.getPoClassFile());
        }
        context.buildPo();
        context.buildVo();
        context.buildService();
    }					
}

Web 环境的代码生成

Web 环境的代码生成本文以 webfull 项目为示例, 它以 WebFullCodeStarter 开始,初始化代码生成器,各模块配置,最后按生成用户指定的模块代码。代码生成的模块配置是按表配置的,一个表对应一套代码。我们先来看一下 WebFullCodeStarter 的代码:

package org.github.foxnic.web.generator.module;

import com.github.foxnic.generator.util.ModuleCodeGenerator;
import org.github.foxnic.web.generator.module.bpm.ExampleReimbursementConfig;
import org.github.foxnic.web.generator.module.mall.ExampleAddressConfig;
import org.github.foxnic.web.generator.module.mall.ExampleGoodsConfig;
import org.github.foxnic.web.generator.module.mall.ExampleOrderConfig;
import org.github.foxnic.web.generator.module.mall.ExampleOrderItemConfig;
/**
* 代码生成启动类
* */
public class WebFullCodeStarter extends ModuleCodeGenerator {
    
    public static void main(String[] args) {
        // 新建启动类对象
        WebFullCodeStarter g=new WebFullCodeStarter();
        // 初始化本次需要生成代码的模块
        g.initModules();
        // 启动
        g.start();
    }
    /**
    * 初始化本次需要生成代码的模块
    * */
    public void initModules() {
        initExampleModules();
        initBPMModules();
    }
    /**
    * 初始化 BPM 示例模块
    * */
    private void initBPMModules() {
        this.addConfig(new ExampleReimbursementConfig());
    }
    /**
    * 初始化订单示例模块
    * */
    private void initExampleModules() {
        this.addConfig(new ExampleGoodsConfig());
        this.addConfig(new ExampleAddressConfig());
        this.addConfig(new ExampleOrderConfig());
        this.addConfig(new ExampleOrderItemConfig());
    }
}

启动 WebFullCodeStarter 的 main 函数,输出如下:

输入 ALL 生成全部

或输入需模块序号: (1) webfull_example_goods (2) webfull_example_address (3) webfull_example_order (4) webfull_example_order_item (5) webfull_example_reimbursement

|

此时,输入 all 或 a 生成列表中全部模块的代码,如果输入指定模块的序号,则社能成指定模块的代码。

从这个动图中,我们不难发现,代码生成器不必重启可以反复生成。针对一些界面的生成,可以边修改代码生成的配置,边生成边测试,我们把它叫做迭代式的代码生成。

为了能够反复进行代码生成,我们针对模块代码的结构做了一些特殊的设计,尽量把业务逻辑的代码剥离到某个单独的文件,其它文件则可以反复生成,这种代码生成的方式极大的提高了开发效率。

代码生成的结果

某个模块的代码生成后,在项目的各个 Maven 子模块下生成相应的 Java 类、Html 文件、js 文件。下面对各个文件说明如下:

文件类型

示例

位置

说明

PO

Order.java

domain

与数据表对应的实体类。

POMeta

OrderMeta.java

domain

针对 PO 类的元数据描述,包含一个 PO Proxy 内部类。

VO

OrderVO.java

domain

用于 API 接口传参、返回等。

VOMeta

OrderVOMeta.java

domain

针对 VO 类的元数据描述,包含一个 VO Proxy 内部类。

自定义模型

CustomModel

domain

代码生成时自定义的模型类。

Proxy

OrderServiceProxy.java

proxy

接口代理、Feign代理接口、接口路径定义。

服务接口

IOrderService.java

service

服务接口

服务实现

OrderServiceImpl.java

service

服务接口实现

流程支持

OrderBpmEventAdaptor.java

service

可选;开启流程审批时会生成,处理流程逻辑。

API控制器

OrderController

service

Rest API 接口控制器

页面控制器

OrderPageController

view

页面控制器

列表页

order_list.html

view

列表页,表格展示数据。

列表页JS

order_list.js

view

列表页对应的JS文件

表单页

order_form.html

view

表单页,表单方式展示、编辑数据。

表单页JS

order_form.js

view

表单页JS

逻辑扩展JS

order_ext.js

view

剥离表单、表格业务逻辑,使页面代码可反复生成。

从上面的表格我们可以看到,一个数据表对用的简单模块,就包含可诸多文件。其中,黑色加粗的几项不建议手动修改,如要修改可以通过代码生成的方式重新生成。

页面如果有业务逻辑,尽量到 xxx_ext.js 文件修改,因为一旦修改也列表页或表单也的代码就无法再次重新生成代码。

项目依赖

Foxnic-Web 代码生成的方式是迭代式的,所以被生成的代码需要依赖到代码生成的项目中。以 webfull 项目的 generator 项目为例,在它的 pom 文件中需要加入 domain、proxy、service 和 view 的依赖。

<dependencies>
  <!-- 通用基础模块 -->
  <dependency>
    <groupId>com.github.foxnic.web</groupId>
    <artifactId>framework-boot</artifactId>
    <version>${foxnic.web.version}</version>
  </dependency>
  <dependency>
    <groupId>com.github.foxnic.web</groupId>
    <artifactId>framework-cloud</artifactId>
    <version>${foxnic.web.version}</version>
  </dependency>
  <dependency>
    <groupId>com.github.foxnic</groupId>
    <artifactId>foxnic-generator</artifactId>
    <version>${foxnic.version}</version>
  </dependency>
  <!-- 当前项目基础模块 -->
  <dependency>
    <groupId>com.github.foxnic.example.webfull</groupId>
    <artifactId>webfull-proxy</artifactId>
    <version>${project.version}</version>
  </dependency>
  <dependency>
    <groupId>com.github.foxnic.example.webfull</groupId>
    <artifactId>webfull-domain</artifactId>
    <version>${project.version}</version>
  </dependency>
  <dependency>
    <groupId>com.github.foxnic.example.webfull</groupId>
    <artifactId>webfull-framework</artifactId>
    <version>${project.version}</version>
  </dependency>
  <!-- 当前项目业务模块 -->
  <dependency>
    <groupId>com.github.foxnic.example.webfull</groupId>
    <artifactId>webfull-service-example</artifactId>
    <version>${project.version}</version>
  </dependency>
  <dependency>
    <groupId>com.github.foxnic.example.webfull</groupId>
    <artifactId>webfull-view-example</artifactId>
    <version>${project.version}</version>
  </dependency>
</dependencies>

小结

本节主要介绍了在 Foxnic-SQL 和 oxnic-Web 代码生成的基本步骤,以及代码生成的文件分布,具体作用等。尤其要注意是代码生成项目的依赖问题,可能导致异常而无法正确生成代码。

后面的章节中,我们将进一步介绍代码生成的细节。

相关项目

foxnic: Foxnic基础框架,开发工具。

foxnic-web: FoxnicWeb是一个应用开发框架,高效、快速、稳定。

Foxnic-EAM固定设备资产管理系统: EAM固定资产设备管理系统,满足中小企业基本需求,对常用资产设备进行信息化管理,包含自定义支持各类设备、自带导入导出、维护工作统计、采购管理、文档管理、合同管理等功能

foxnic-samples: 用于展示和提供 Foxnic 相关的示例工程和代码

官方文档

Foxnic 开发文档 - 入门指南

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值