后端项目开发:代码生成的思想与实现

代码生成要解决什么问题?

大部分项目里其实有很多代码都是重复的,几乎每个基础模块的代码都有增删改查的功能,而这些功能都是大同小异, 如果这些功能都要自己去写,将会大大浪费我们的精力降低效率。所以,这种重复性的代码可以使用代码生成,提高开发效率。

以传统的MVC开发架构为例,会有controller,service,model,repository的四层划分,简单的单表的增删查改,若采用手动编码,就要关注4x4=16处代码的编写。这是每个模块几乎避免不了的部分。若数据库表增加了一个字段,那又要关注4x4=16处代码的对错。

代码生成不能解决什么问题?

代码生成不能生成所有类型的代码。它最适合生成简单、重复性高的代码。对于复杂多变、独特的代码,代码生成可能不适用。

如何进行代码生成?

开发人员可以通过定义模板和规则,快速生成大量的代码。

在领域驱动设计中,开发人员可以设计符合现实情况的实体与行为,这是代码经常动刀的位置,可能不适合进行代码生成。

若使用JPA的技术可以根据实体自动生成数据库表。但是该种方式不适合多表关联查询,同时有点风险。(如果已有的数据库表结构与JPA实体定义不一致,自动生成表时可能会导致数据丢失或冲突)

在中国,报表的查询需求也是一大重点。所以建议使用mybatis的ORM框架,这个也是中国使用人数占大多数的技术栈。恰好有个代码生成工具——mybatis-generate可以提前设计好数据库表,再根据数据库表生成相应的代码。

因此,我们不得不基于数据库表驱动的方式进行代码生成。

若觉得使用mybatis-generate的工具不够灵活,那么可以选择使用Velocity工具自行编辑template模板生成对应的代码文件。

长远打算的话,我建议还是用Velocity自行配置代码模板。

一、数据库表结构

若采用数据驱动设计的方式构建生成代码,则需要设计数据库表。

根据数据库表的组织形式,数据库表结构分为:

单表结构(不包含任意外键)

没有任何外键的单表结构

新建数据库表结构(单表)

drop table if exists sys_student;
create table sys_student (
  student_id           int(11)         auto_increment    comment '编号',
  student_name         varchar(30)     default ''        comment '学生名称',
  student_age          int(3)          default null      comment '年龄',
  student_hobby        varchar(30)     default ''        comment '爱好(0代码 1音乐 2电影)',
  student_sex          char(1)         default '0'       comment '性别(0男 1女 2未知)',
  student_status       char(1)         default '0'       comment '状态(0正常 1停用)',
  student_birthday     datetime                          comment '生日',
  primary key (student_id)
) engine=innodb auto_increment=1 comment = '学生信息表';

树表结构(表中含父ID字段)

自身与自身有上下层级关系的树表结构

新建数据库表结构(树表)

drop table if exists sys_product;
create table sys_product (
  product_id        bigint(20)      not null auto_increment    comment '产品id',
  parent_id         bigint(20)      default 0                  comment '父产品id',
  product_name      varchar(30)     default ''                 comment '产品名称',
  order_num         int(4)          default 0                  comment '显示顺序',
  status            char(1)         default '0'                comment '产品状态(0正常 1停用)',
  primary key (product_id)
) engine=innodb auto_increment=1 comment = '产品表';

主子表结构

数据与其他表有关联关系的主子表结构,是一对多的关系
新建数据库表结构(主子表)

-- ----------------------------
-- 客户表
-- ----------------------------
drop table if exists sys_customer;
create table sys_customer (
  customer_id           bigint(20)      not null auto_increment    comment '客户id',
  customer_name         varchar(30)     default ''                 comment '客户姓名',
  phonenumber           varchar(11)     default ''                 comment '手机号码',
  sex                   varchar(20)     default null               comment '客户性别',
  birthday              datetime                                   comment '客户生日',
  remark                varchar(500)    default null               comment '客户描述',
  primary key (customer_id)
) engine=innodb auto_increment=1 comment = '客户表';


-- ----------------------------
-- 商品表
-- ----------------------------
drop table if exists sys_goods;
create table sys_goods (
  goods_id           bigint(20)      not null auto_increment    comment '商品id',
  customer_id        bigint(20)      not null                   comment '客户id',
  name               varchar(30)     default ''                 comment '商品名称',
  weight             int(5)          default null               comment '商品重量',
  price              decimal(6,2)    default null               comment '商品价格',
  date               datetime                                   comment '商品时间',
  type               char(1)         default null               comment '商品种类',
  primary key (goods_id)
) engine=innodb auto_increment=1 comment = '商品表';

分析

单表

以学生表为例,对于管理学生列表,只需要单表查询学生信息就够了。但是在表中含有状态、性别等表现为选择类型的字段时,有三种实现方式:枚举类型,字典表和数字表示

  1. 使用枚举类型:可以为状态、性别等字段创建对应的枚举类型,在数据库中存储对应的枚举值。在Java代码中,将枚举类型与数据库字段进行映射,可以方便地进行查询和处理。

  2. 使用字典表:可以创建一个字典表,用于存储状态、性别等选择类型的所有可能取值。在学生表中,将状态、性别等字段设计为外键,关联字典表的主键。这样可以在查询学生信息时,通过关联查询获取对应的状态、性别等具体值。

  3. 使用字符或数字表示:可以直接在学生表中使用字符或数字表示状态、性别等选择类型的值。例如,使用字符 ‘0’ 表示男性,‘1’ 表示女性,‘2’ 表示未知。这种方式简单直接,但可读性较差,需要在代码中进行相应的解析和转换。

枚举类型和字典表可以提供更好的可读性和扩展性,但可能增加一定的复杂性。使用字符或数字表示则较为简单,但可读性较差。

二、视图表现形式

尽管是根据数据表的字段来自动生成代码,但要符合现实的业务逻辑的话,还是要根据用例来定义各种模板和规则。

数据库表的设计是要根据视图页面的各种呈现方式来进行的。

考虑各种数据在视图中的展现方式:

  • 表格(table)。针对大量数据的查询问题,应该设计表格的视图形式。表格以不同的维度进行分类、排序、展示,方便用户快速查找和定位数据。 表格的展现方式更适合于数据量较大的数据库表,例如用户表、客户表、订单表、产品表等

  • 列表(List)。列表是将数据以一维线性的方式展示,通常用于展示数据的列表、目录、菜单等。列表的表现方式更适合于数据量较少的表,例如角色表、权限表、部门表、用户表等。

  • 树状图(tree)。针对层级关系的问题,应该设计树状图的视图形式。数据库表例如部门表、分类表、工作事项表会采用此展现方式。

  • 甘特图(Gantt chart)。针对时间进度展示的问题,应该设计甘特图的视图形式。方便用户在时间进度的维度来管理和决策。数据库表例如项目进度表等含时间字段的表会采用此表现形式。

  • 看板(board)。针对流转状态的问题,应该设计看板的视图形式。方便用户管理决策,并且通过简单的拖拽变更状态,进一步助力用户提效。数据库表中的工作流表、工单表、任务表通常会采用看板的展现方式,以便用户根据工作的不同状态进行查看和管理。

  • 表单(form)。表单在网页中主要负责数据采集功能。表单通常由表单标题、表单域、表单按钮等组成。表单域用于输入数据,表单按钮用于提交表单。表单可以用于收集用户的个人信息、订单信息等。

参考资料:
B端界面设计:查询表格页面
B端界面设计:页面分类设计

1.页面类型

考虑页面类型,如果是分发引导页面,该类页面数量较少,也不会为分发引导页面专门创建数据库表,因此不考虑对分发引导页面进行代码生成,应该由开发人员根据界面设计自由调整并编写代码。

如果是配置页面,例如菜单展示、权限控制等配置页面,该类页面数量不多,使用频率也不高,尽管会对配置页面专门创建数据库表,但是建议还是由开发人员自行编写代码。

如果是操作页面,这是用户使用频率和使用时长最高的页面,数量最多的页面,它是对各种资源的操作,数据库表也是为此创建的,代码生成主要就是针对此类页面。

2.页面分析

(1)首先考虑需要所要显示的资源的数据量
如果是一个屏幕就能展示的数据,就考虑使用列表,树状图,看板等来实现。
如果是不能一个屏幕展示,就需要考虑分页,这时候要用表格或其变种来实现。
(2)再考虑数据之间的联系
对于一个屏幕能展示的数据:

  • 若数据之间没关联,可以用列表来实现;
  • 若数据之间有上下级分层的关系,可以用树状图来实现;
  • 数据之间有流转关系,可以用看板来实现。

对于一个屏幕不能展示的数据,可以用表格列表来实现

  • 信息量较少的情况,可以用折叠展开的方式查看更进一步的详情。
  • 数据详情体量不大,页面内容较轻时。同时,不需要参照上级页面内容,有快速回退的诉求。可以用弹窗实现。
  • 详情页的内容较多时,且有快速切换主体的诉求。可以用抽屉实现。
  • 当详情页承载内容过多且里面的操作相对复杂时,如详情页内有表格的嵌套和特别多的操作。可以用新增页面实现。

(3)考虑操作的意图
例如,表格的主要用途:

对于数据操作类型的页面来说更加依赖查询功能,也就是说数据操作类的页面需要更加精细的搜索的页面,因为接下来用户需要根据设置的各种精确搜索条件筛选出符合业务条件的一批数据进行批量操作。
一般此类页面的数据量都有数千条记录以上,需要多条件进行筛选数据

数据维护类是对一批数据进行新增和维护,所以该页面的核心目标是新增数据,对于新增数据来说,搜索旧数据变成了低频的使用需求,这时候精细搜索功能对于该页面就不是特别有必要。
一般此类页面的数据量都有几十到数百条记录,只需要模糊搜索。

三、接口方法

在进行代码的自动生成时,接口方法重复的不仅有增删查改方法。若进行细分,可能还会有以下方法。

接口方法

对于单表的方法,例如学生表,可能会有这些方法:

  1. POST /students:插入新的学生记录。
  2. PUT /students/{studentId}:更新指定学生编号的学生记录。
  3. DELETE /students/{studentId}:删除指定学生编号的学生记录。
  4. GET /students/{studentId}:获取指定学生编号的学生记录。
  5. GET /students:查询学生记录,可以通过查询参数指定条件进行筛选。
  6. GET /students?page={page}&size={size}:分页查询学生记录。
  7. GET /students?sort={field}&order={asc/desc}:排序查询学生记录。
  8. GET /students/stats:统计学生记录的相关信息。
  9. POST /students/batch:批量插入学生记录。
  10. PUT /students/batch:批量更新学生记录。
  11. DELETE /students/batch:批量删除学生记录。
  12. PUT /students?condition={condition}:根据条件更新学生记录。
  13. DELETE /students?condition={condition}:根据条件删除学生记录。
  14. GET /students?keyword={keyword}:根据关键字模糊查询学生记录。
  15. GET /students/group?field={field}:根据字段对学生记录进行分组查询。
  16. GET /students/{studentId}/details:查询指定学生编号的学生详细信息和对应的班级信息。

对于树表的方法,例如产品表,可能会有这些方法:

  1. POST /products:插入新的产品记录。
  2. PUT /products/{productId}:更新指定产品编号的产品记录。
  3. DELETE /products/{productId}:删除指定产品编号的产品记录。
  4. GET /products/{productId}:获取指定产品编号的产品记录。
  5. GET /products?parentId={parentId}:获取指定父产品的所有子产品。
  6. GET /products/{productId}/parent:获取指定产品的父产品。
  7. GET /products/tree:获取整个产品表的树形结构。
  8. PUT /products/{productId}/move?parentId={parentId}:将指定产品节点移动到另一个父节点下。
  9. GET /products?sort=order_num&order={asc/desc}:排序查询产品记录。
  10. GET /products/{parentId}/descendants:获取指定父产品的所有子孙产品。
  11. GET /products/{productId}/siblings:获取指定产品的同级兄弟产品。
  12. GET /products/roots:获取所有顶级根节点产品。
  13. GET /products/{productId}/path:获取指定产品到根节点的路径。
  14. GET /products/{parentId}/child-count:统计指定父产品的子节点数量。
  15. DELETE /products/batch:批量删除产品记录。
  16. PUT /products/batch:批量更新产品记录。
  17. GET /products/leaves:获取所有叶子节点产品。
  18. GET /products/level/{level}:查询指定层级的产品节点。
  19. POST /products/batch:批量插入产品记录。
  20. GET /products?keyword={keyword}:根据关键字模糊查询产品记录。
  21. GET /products?page={page}&size={size}:分页查询产品记录。
  22. GET /products/stats:统计产品记录的相关信息。

对于主子表结构,例如客户表和商品表,可能的方法有:

  1. POST /customers:插入新的客户记录。
  2. PUT /customers/{customerId}:更新指定客户编号的客户记录。
  3. DELETE /customers/{customerId}:删除指定客户编号的客户记录。
  4. GET /customers/{customerId}:获取指定客户编号的客户记录。
  5. GET /customers/{customerId}/goods:获取指定客户的所有关联商品记录。
  6. POST /goods:插入新的商品记录。
  7. PUT /goods/{goodsId}:更新指定商品编号的商品记录。
  8. DELETE /goods/{goodsId}:删除指定商品编号的商品记录。
  9. GET /goods/{goodsId}:获取指定商品编号的商品记录。
  10. GET /goods/{goodsId}/customer:获取指定商品的关联客户记录。
  11. GET /customers:查询客户记录,可以通过查询参数指定条件进行筛选。
  12. GET /goods:查询商品记录,可以通过查询参数指定条件进行筛选。
  13. GET /customers?page={page}&size={size}:分页查询客户记录。
  14. GET /goods?page={page}&size={size}:分页查询商品记录。
  15. GET /customers?sort={field}&order={asc/desc}:排序查询客户记录。
  16. GET /goods?sort={field}&order={asc/desc}:排序查询商品记录。
  17. GET /customers/stats:统计客户记录的相关信息。
  18. GET /goods/stats:统计商品记录的相关信息。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值