一种基于数据库+模板渲染的代码生成器——表结构暂存与类设计

接上篇

在上一篇文章《一种基于数据库+模板渲染的代码生成器——简介及数据库查询》中,我们介绍了代码生成器的基本概念以及常见的几种代码生成器,并阐述了通过mysql数据库中的information_schema库,查询数据库中的表结构、列结构等信息。

本篇文章将继续阐述,查询出表结构与列结构后,如何将其逐步映射为编程语言Java中的类及对象。

表与列结构暂存

在若依系统中,其实现代码生成分为两个步骤,第一步被称为“导入表”,即将所要生成代码的表的结构信息存储到到某几个指定表gen_tablegen_table_column中。

其注释分别为:

表名 注释
gen_table 代码生成业务表
gen_table_column 代码生成业务表字段

先来看一下gen_table存储了哪些信息:

执行以下SQL:

1
2
3
4
5
6
7
8
9
SELECT
COLUMN_NAME,
COLUMN_COMMENT,
DATA_TYPE
FROM
information_schema.`COLUMNS`
WHERE
TABLE_NAME = 'gen_table'
AND TABLE_SCHEMA = 'ry-vue'

查询结果如下图:

image-20220106165724085

再来看gen_table_column

1
2
3
4
5
6
7
8
9
SELECT
COLUMN_NAME,
COLUMN_COMMENT,
DATA_TYPE
FROM
information_schema.`COLUMNS`
WHERE
TABLE_NAME = 'gen_table_column'
AND TABLE_SCHEMA = 'ry-vue'

执行结果如下:

image-20220106165833766

在Java中,分别按照表gen_tablegen_table_column构建两个实体对象,即Entity,以便后续通过mybatis等对其进行保存等相关操作。

我们截图部分源码代码(其所属位置位于若依项目:ruoyi-generator/src/main/java/com/ruoyi/generator/domain路径下):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class GenTable extends BaseEntity
{
private static final long serialVersionUID = 1L;

/** 编号 */
private Long tableId;

/** 表名称 */
@NotBlank(message = "表名称不能为空")
private String tableName;

/** 表描述 */
@NotBlank(message = "表描述不能为空")
private String tableComment;

/** 关联父表的表名 */
private String subTableName;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class GenTableColumn extends BaseEntity
{
private static final long serialVersionUID = 1L;

/** 编号 */
private Long columnId;

/** 归属表编号 */
private Long tableId;

/** 列名称 */
private String columnName;

/** 列描述 */
private String columnComment;
}

表名列名与类名字段名的转换

在获取到表结构与列结构数据后,其名称大多数是不符合类名创建规则的,需要做一个额外的转换,如下图:

表名与列名 Java类名与字段名
表名:sys_my_user MyUser
列名:user_name userName

对于列名,将其转换为驼峰命名法;对于表名,一是注意需要去掉某些指定前缀,二是将剩余部分转换为首字母大写的驼峰。

对于转换为驼峰的方式,我在若依系统源码中发现有两个不同的实现方式,供大家参考:

第一种,从字符串开头,通过大写标识依次拼接字符:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

/*com.ruoyi.common.utils.StringUtils*/

private static final char SEPARATOR = '_';
/**
* 转换为驼峰命名法
*/
public static String toCamelCase(String s)
{
if (s == null)
{
return null;
}
s = s.toLowerCase();
StringBuilder sb = new StringBuilder(s.length());
boolean upperCase = false;
for (int i = 0; i < s.length(); i++)
{
char c = s.charAt(i);
if (c == SEPARATOR)
{
upperCase = true;
}
else if (upperCase)
{
sb.append(Character.toUpperCase(c));
upperCase = false;
}
else
{
sb.append(c);
}
}
return sb.toString();
}

第二种,按照下划线拆分原始字符串后,依次首字母大小,然后拼接:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/*com.ruoyi.common.utils.StringUtils*/

public static String convertToCamelCase(String name)
{
StringBuilder result = new StringBuilder();
// 快速检查
if (name == null || name.isEmpty())
{
// 没必要转换
return "";
}
else if (!name.contains("_"))
{
// 不含下划线,仅将首字母大写
return name.substring(0, 1).toUpperCase() + name.substring(1);
}
// 用下划线将原始字符串分割
String[] camels = name.split("_");
for (String camel : camels)
{
// 跳过原始字符串中开头、结尾的下换线或双重下划线
if (camel.isEmpty())
{
continue;
}
// 首字母大写
result.append(camel.substring(0, 1).toUpperCase());
result.append(camel.substring(1).toLowerCase());
}
return result.toString();
}

数据类型的转换

即数据库中定义的数据类型包括:varchar, int, datetime, decimal等等,这些都需要一一映射为Java中的相关对象,包括String, Integer, Date, BigDecimal等等。

暂存结果:

比如我们的表my_user,结构信息如下:

image-20220106172732427

经过转换,存储在gen_table以及gen_table_column中的数据为:

image-20220106172907984

image-20220106172927791

总结

我们便可不需要通过information_schema数据库查询信息,而仅需要通过这两个gen_表数据生成对应的类信息。通过其存储数据,我们也可以看到,表中存储了构建对象属性的类型、名称,这也便于后续直接生成对象,而不需要再次将其转换,减轻了一步操作的系统压力。

通过使用表“缓存”数据结构,的确是十分高明的方法,值得学习!