Java 代码生成器
本文最后更新于 2022年12月12日 早上
概述
代码生成器的主要目的是提高开发效率,减少重复劳动,并确保生成的代码符合一定的规范。
可以使用它来自动生成那些具有一定规律性的代码,包括但不限于数据库访问层、业务逻辑层和用户界面层。
它可以根据给定的数据库和表信息,使用模板生成代码,这个过程包括以下步骤:
1、确定需要生成的代码。
2、编写代码生成器的逻辑。
3、使用模板引擎来帮助你生成代码。
4、编写工具来调用你的代码生成器,并将生成的代码写入文件。
实现思路
参考:自定义代码生成器。[1]
使用 Java 中的 JDBC API 来连接到数据库,并使用 SQL 查询或
DatabaseMetaData
接口来获取数据库表的元数据,例如表名、列名和数据类型。使用模板引擎(如 Freemarker 或 Velocity)来定义代码生成模板。模板可能包括普通文本和特殊标记,用于在生成代码时插入元数据。
使用 Java 代码从模板引擎加载模板,并将元数据插入模板中。最后,将生成的代码写入文件或输出到控制台。
模板引擎
参考:FreeMarker 快速入门。[2]
FreeMarker 是一款用 java 语言编写的模版引擎,
它通过提供一组指令来处理模板文件,并将数据和模板文件合并成最终的文件。
FreeMarker 模板文件可以包含特殊的指令,这些指令可以控制模板的渲染方式,例如条件判断、循环语句等。
程序员可以通过在模板文件中插入这些指令来定制模板的渲染方式。
特点:
轻量级模版引擎,不需要 Servlet 环境就可以很轻松的嵌入到应用程序中。
能生成各种文本,如 html,xml,java 等。
入门简单,它是用 java 编写的,很多语法和 java 相似。
工作原理:
使用步骤:
加载 FreeMarker 模板引擎的类库。
设置模板文件的路径和存储数据的 Map 对象。
创建 FreeMarker 模板引擎的
Configuration
对象。创建模板文件的模板对象。
程序可以通过调用 FreeMarker 的
process
方法,将模板文件和数据进行合并,并将合并后的结果通过 IO 流输出到指定的文件。
数据库元数据
参考:使用 JDBC 提取数据库元数据。[3]
Java 数据库元数据(DatabaseMetaData
)是一个接口,它提供了有关数据库的元数据(即数据库的数据字典)的信息。
它可以用来获取表详细信息、获取列详细信息、获取主键详细信息等。
要使用数据库元数据,需要先使用 JDBC(Java Database Connectivity,Java 数据库连接)连接数据库,然后通过 Connection
对象的 getMetaData
方法获取 DatabaseMetaData
对象。
获取表信息:
getTables(...)
。获取列信息:
getColumns(...)
。获取主键信息:
getPrimaryKeys(...)
。
下面是一个简单的示例,展示了如何使用数据库元数据获取表详细信息、获取列详细信息、获取主键详细信息:
1 |
|
表结构
创建名为 test 数据库,执行以下 sql 语句创建 user 表。
1 |
|
项目结构
spring-boot-code-generator
│
├─src
│ ├─main
│ │ ├─java
│ │ │ └─com
│ │ │ └─example
│ │ │ └─codegenerator
│ │ │ │ SpringBootCodeGeneratorApplication.java
│ │ │ │
│ │ │ ├─common
│ │ │ │ BaseDao.java
│ │ │ │ BaseDaoImpl.java
│ │ │ │ Page.java
│ │ │ │ ResultModel.java
│ │ │ │
│ │ │ ├─config
│ │ │ │ DataSourceConfig.java
│ │ │ │
│ │ │ ├─constant
│ │ │ │ DbConstant.java
│ │ │ │ TemplateConstant.java
│ │ │ │
│ │ │ ├─param
│ │ │ │ ColumnParam.java
│ │ │ │ TableParam.java
│ │ │ │ TemplatePathParam.java
│ │ │ │
│ │ │ └─utils
│ │ │ CodeGenerateUtils.java
│ │ │ DbUtils.java
│ │ │ FreeMarkerTemplateUtils.java
│ │ │ StrUtils.java
│ │ │
│ │ └─resources
│ │ │ application.properties
│ │ │
│ │ └─templates
│ │ controller.ftl
│ │ dao.ftl
│ │ entity.ftl
│ │ param.ftl
│ │ service.ftl
│ │ serviceImpl.ftl
│ │
│ └─test
│ └─java
│ └─com
│ └─example
│ └─codegenerator
│ SpringBootCodeGeneratorApplicationTests.java
依赖配置
在项目的 pom.xml 的 dependencies 中导入需要的依赖包:
1 |
|
全局配置
在项目的 application.properties 文件里添加所需配置:
1 |
|
编码
1、常量类
1.1、数据库常量
该类定义了三个数组常量:
COLUMNTYPE_TIME
、COLUMNTYPE_NUMBER
,分别用于存储数据库时间类型和数字类型。还定义了四个字符串常量:
TYPE_STRING
、TYPE_INTEGER
、TYPE_LONG
和TYPE_BIGDECIMAL
,用于表示不同类型的数据。例如,
TYPE_STRING
表示字符串类型,TYPE_INTEGER
表示整型,TYPE_LONG
表示长整型,TYPE_BIGDECIMAL
表示高精度计算类型。
点击查看代码
1 |
|
1.2、模板常量
该类中包含了几个静态常量,这些常量定义了一些模板的文件路径。通过这些常量,可以引用不同的模板文件。
例如,
ENTITY_TEMPLATE
常量指向了一个实体类的模板文件,DAO_TEMPLATE
常量指向了 dao 的模板文件,以此类推。
点击查看代码
1 |
|
2、参数类
2.1、字段参数
该类用于封装从数据库表中读取的字段信息。
该类包含了字段名、字段类型、字段注释、主键信息以及 Java 类型等信息。
通过这些信息,可以在后续的代码生成过程中使用这些信息。
例如,在生成实体类时,可以使用字段名、字段类型以及 Java 类型来生成对应的属性和 getter/setter 方法。
点击查看代码
1 |
|
2.2、表参数
该类用于封装从数据库表中读取的表信息。
该类包含了表名、表注释、表中数据的日期时间格式、是否包含日期字段、是否包含浮点型字段以及表主键类型等信息。
通过这些信息,可以在后续的代码生成过程中使用这些信息。
例如,在生成实体类时,可以根据表主键类型来生成对应的主键属性。
点击查看代码
1 |
|
2.3、模板参数
该类用于封装代码生成过程中需要使用的一些路径信息。
该类包含了包名、项目名称、作者、生成代码的基础路径以及各个模板文件生成的绝对路径等信息。
通过这些信息,可以在后续的代码生成过程中使用这些信息。
例如,在生成实体类时,可以使用实体类生成的绝对路径来指定实体类文件的存储路径。
点击查看代码
1 |
|
3、工具类
3.1、代码生成器工具类
该类主要用于根据给定的 FreeMarker 模板和数据模型生成代码。
generate()
方法接受表名作为输入,并从数据库中检索相应的表信息。然后,它使用此信息基于指定的 FreeMarker 模板为应用程序的不同层生成代码(例如实体,DAO,服务,控制器)。生成的代码写入指定的输出目录中的文件系统。该类期望模板位于 classpath 目录下,并使用
FreeMarkerTemplateUtils
类加载模板。
点击查看代码
1 |
|
3.2、数据库工具类
该类主要用于连接数据库、获取数据库信息,如表名、字段名、字段类型等,并封装成对象返回。
该类中主要用到了 JDBC 连接数据库的 API,包括
DriverManager.getConnection()
方法用于获取数据库连接,DatabaseMetaData
类中的方法用于获取数据库元数据信息。其中
getAllTables()
方法用于获取所有表名及注释,getAllColumns()
方法用于获取某张表的所有列信息,getJavaType()
方法用于将数据库字段类型转换成 Java 类型。需要注意的是,该类中使用到的配置信息,例如数据库链接地址、用户名、密码等,需要通过读取
application.properties
文件获取,读取文件使用了第三方库hutool
的 Props 类。
点击查看代码
1 |
|
3.3、FreeMarker 工具类
该类用于加载并获取 FreeMarker 模板文件。
FreeMarker 是一种模板引擎,可以通过模板文件来生成静态文本,模板文件中可以使用 FreeMarker 的指令和数据变量来动态控制文本的生成。
FreeMarkerTemplateUtils
类中,CONFIGURATION
对象表示一个 FreeMarker 模板配置,该对象用于设置 FreeMarker 的各种属性,包括模板文件的加载方式、编码方式、异常处理方式等。getTemplate()
方法用于根据模板文件名称获取一个Template
对象,该对象表示一个 FreeMarker 模板文件,可以通过它来生成静态文本。在这个工具类中,模板文件的加载方式采用的是
FileTemplateLoader
类,即从 classpath 目录下加载模板文件。
点击查看代码
1 |
|
3.4、字符串工具类
该类包含两个方法:
changeColumnStr
和changeTableStr
。changeColumnStr
方法用于将一个带下划线的字符串转换为驼峰命名法,例如将 “user_name” 转换为 “userName”。changeTableStr
方法用于将一个带下划线的字符串转换为驼峰命名法,并且首字母大写,例如将 “tb_user” 转换为 “TbUser”。两个方法都会通过递归调用来处理多个下划线的情况。
点击查看代码
1 |
|
制作通用模板
在 resources/templates 目录下创建模版文件,模板内容可自定义成你所需要的。
1、entity 模版
该模板中使用了 Freemarker 模板语言的语法,通过传入的参数(例如表名、字段信息等)来生成对应的实体类代码。
例如,在模板中可以使用
${package_name}
来表示包名,使用${table_name}
来表示实体类名称,使用${model_column}
来表示表中所有的字段信息。通过这些变量,可以生成完整的实体类代码。例如,通过循环遍历
${model_column}
中的每一个字段信息,可以生成对应的属性和 getter/setter 方法。
点击查看代码
1 |
|
2、dao 模板
该模板中使用了 Freemarker 模板语言的语法,通过传入的参数(例如包名、实体类名等)来生成对应的 Dao 类代码。
该 Dao 类继承自
BaseDaoImpl
,并在构造函数中调用父类的构造函数传入实体类的类型,以便在操作数据库时获取到实体类的信息。该 Dao 类还使用了
@Repository
注解,用于将该类标识为 Spring 的数据访问对象。
点击查看代码
1 |
|
3、param 模板
该模板中使用了 Freemarker 模板语言的语法,通过传入的参数(例如包名、表名等)来生成对应的查询参数类代码。
该查询参数类实现了
Serializable
接口,表示该类的对象可以被序列化。该查询参数类中定义了与数据库表中列名相同的属性,并提供了对应的 getter 和 setter 方法。
该类还使用了
@JsonFormat
和@DateTimeFormat
注解来格式化日期类型的属性。
点击查看代码
1 |
|
4、service 模版
这段代码定义了一个接口,用于创建一个服务类,该服务类可以可以用来对名为
${table_name}
的实体执行 CRUD(创建、读取、更新、删除)操作。该服务暴露了几个方法,例如
list
、getById
、save
、updateById
和removeById
,它们分别允许用户获取实体列表、根据 ID 获取单个实体、创建新实体、更新现有实体和删除实体。
服务方法都会返回一个
ResultModel
对象,其中包含操作结果。ResultModel
类是一个自定义类,它提供了一种统一的方式来表示应用程序中操作的结果。
点击查看代码
1 |
|
5、service 实现类模版
这段代码定义了一个前面定义的
${table_name}Service
接口的具体实现。该类使用了
@Service
和@Transactional
注解,表示它是一个 Spring 服务 Bean ,并且它使用了 Spring 框架的事务支持。这意味着这个类中使用了
@Transactional
注解的任何方法都将在数据库事务中执行。该类的构造函数接收一个
${table_name}Dao
实例,这个实例可能是一个数据访问对象(DAO)类,用于对${table_name}
实体执行数据库操作。该类实现了
${table_name}Service
接口定义的方法,为每个方法提供了具体的实现。例如,
list
方法使用 DAO 执行一个 SQL 查询,从数据库中使用提供的Page
对象分页来检索${table_name}
实体的列表。然后该方法返回一个包含实体列表的
ResultModel
对象。类中的其他方法也都使用了 DAO 来在数据库中对
${table_name}
实体执行 CRUD 操作,并返回一个包含操作结果的ResultModel
对象。
点击查看代码
1 |
|
6、controller 模版
这段代码是一个 Java 控制器,它提供了一系列用于管理
${table_name}
实体的 RESTful API 接口。具体来说,它提供了以下接口:
/list
:用于获取${table_name}
实体的分页列表。/read/{id}
:用于获取一个${table_name}
实体的详细信息。/create
:用于新建${table_name}
实体。/update
:用于更新${table_name}
实体。/delete/{id}
:用于删除${table_name}
实体。
该控制器使用
@RestController
注解声明了它是一个 RESTful 控制器,并通过@RequestMapping("/${table_name_small}")
注解指定了它的基础路径为/${table_name_small}
。除了接口的基础路径之外,每个接口还有一个方法级别的路径。例如,新增接口的完整路径为
/${table_name_small}/create
。每个接口都有一个对应的方法,该方法实现了接口的具体逻辑。每个方法都通过
@GetMapping
、@PostMapping
或@DeleteMapping
注解来指定它的 HTTP 方法和路径。该控制器还使用了
@Validated
和@PathVariable
注解,分别用于对参数进行验证和从路径中获取参数值。
点击查看代码
1 |
|
测试
- 执行代码生成器工具类
CodeGenerateUtils
输出如下。
1 |
|
- 文件创建成功,相关模块文件如下。
- 启动项目测试刚生成的模块接口是否正常,调用增删改查接口如下。