序言:上篇主要优化完善公共模块,本篇主要创建一个生成代码的独立模块,提升开发效率,避免繁琐的重复的crud操作。由于内容较多,我就分两节写了。本节我们主要以创建项目并简单的生成数据库实体类即可,下节我们会直接搞完。
Freemarker是什么
Freemarker 是一个基于 Java 的模板引擎,它主要用于动态生成文本输出,广泛应用于 Web 应用程序、配置文件生成、代码生成等场景。它通过模板和数据模型的结合,能够将数据内容动态地插入到预定义的模板中,生成最终的输出结果。
主要特点:
1. 基于模板的文本生成: Freemarker 使用模板文件,模板中包含占位符,最终的数据内容会根据模板和数据模型的匹配进行动态生成。这种方式使得生成的内容具有高度的灵活性,可以根据不同的需求生成不同的结果。
2. 与 Java 无缝集成: Freemarker 是 Java 编写的,因此它可以轻松与 Java 项目集成,并且可以通过 Java 代码将数据模型传递给模板,从而生成动态内容。
3. 高效且灵活的模板语言: Freemarker 提供了一种专门的模板语言,具有强大的功能,包括条件判断、循环、宏、内置函数等,允许在模板中进行复杂的逻辑操作。
4. 支持多种输出格式: Freemarker 可以生成多种格式的输出,例如 HTML、XML、JSON、配置文件、代码文件等。通过定义不同的模板,Freemarker 能够处理各种不同类型的输出需求。
5. 代码生成的利器: Freemarker 被广泛应用于自动化生成代码的场景,尤其在需要快速生成 CRUD 操作、API 接口文档、配置文件等时,Freemarker 可以极大提高开发效率,减少重复工作。
典型应用场景:
1. Web 页面渲染: Freemarker 经常与 Web 框架(如 Spring MVC)结合使用,用于生成动态的 HTML 页面。例如,可以根据用户的输入或者从数据库中查询到的数据来生成网页内容。
2. 生成配置文件: Freemarker 可以根据传入的配置模型自动生成配置文件,如 XML 配置文件、properties 文件等。这对于一些需要根据不同环境或条件生成配置文件的场景非常实用。
3. 自动化代码生成: 通过 Freemarker,可以根据数据库表结构、配置文件或其他输入自动生成代码(如 Java 类、数据库操作代码等)。例如,使用 Freemarker 生成 Java 的实体类、DAO 层代码、Service 层代码等。
4. 电子邮件模板: Freemarker 可用于生成动态电子邮件内容,例如在发送通知邮件时,基于不同的收件人生成不同内容的邮件模板。
Freemarker 是一个功能强大的模板引擎,适用于多种场景,特别是在动态生成文本(如代码、HTML、配置文件等)时非常高效。它通过模板和数据模型的结合,提供了灵活、可扩展的输出方式,能有效提高开发效率,减少重复工作,是 Java 开发中常用的工具之一。
创建模块
介绍完之后直接进入我们最主要的代码模块。
右键父项目创建模块,不是子模块哦,如下图
注意父项选择:<None>
打开它的POM文件,直接放依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.4</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.6</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>3.5.5</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>3.0.0</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
连接数据库我们只要使用的是MyBatis-Plus,所以这里也用,当然也可以用MyBatis,当然也可以不用,不用的话就需要自己创建表列集合了。
创建包com.shine.generator
(本文以下全部建成父包
)
创建启动类GeneratorApplication
,代码如下
@SpringBootApplication
public class GeneratorApplication {
public static void main(String[] args) {
SpringApplication.run(GeneratorApplication.class, args);
}
}
在resource
下创建配置文件application.yml
generator:
datasource:
url: jdbc:mysql://localhost:3306/你的库?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8&useUnicode=true
username: root
password: 你的密码
# 包配置
package-config:
modal-name: 你的模块名
base-package: com.shine
# 生成配置
generator:
database: database
table-name:
- u_table_name
table-prefix: 表前缀,没有不填
# 需要生成的
code:
- entity
- enum
- info
- page_request
- create_request
- update_request
- mapper
- mapper_xml
- service
- service_impl
- controller
# 这里是生成的方法
methods:
- page
- get
- delete
# 数据库连接配置
spring:
datasource:
url: ${generator.datasource.url}
username: ${generator.datasource.username}
password: ${generator.datasource.password}
在父包
下创建包properties
,并且创建类GeneratorProperties
,内容如下:
@Data
@Component
@ConfigurationProperties(prefix = "generator")
public class GeneratorProperties {
private Datasource datasource;
private Package packageConfig;
private Generator generator;
@Data
public static class Datasource {
private String url;
private String username;
private String password;
}
@Data
public static class Package {
private String modalName;
private String basePackage;
}
@Data
public static class Generator {
private String database;
private List<String> tableName;
private String tablePrefix;
private List<GeneratorEnum> code;
private List<MethodEnum> methods;
}
}
主要是读取
application.yml
的配置
在父包
下创建包entity
,创建实体类Column
和Table
,内容如下
@Data
public class Column {
private String columnName;
private String fieldName;
private String dataType;
private String javaType;
private String comment;
}
@Data
public class Table {
private String tableName;
private String className;
private String lowercaseClassName;
private String entityName;
private String comment;
private List<Column> columnList;
private String generatorDate;
private String author = "huihui";
private String packagePath;
private String moduleName;
}
在父包
下创建包model
,创建类EnumModel
和MethodModel
,内容如下
@Data
public class EnumModel {
private String author = "huihui";
private String packagePath;
private String moduleName;
private String className;
private String generatorDate;
private List<Item> contentList;
@Data
public static class Item {
private String value;
private String code;
private String name;
private String comment;
}
}
@Data
public class MethodModel extends Table {
private String infoName;
private List<MethodEnum> methodList;
private String datasourceName;
}
在父包
下创建包conver
,创建类型转换类TypeConvert
,内容如下
@Component
public class TypeConvert {
public void convert(List<Column> columnList) {
columnList.forEach(item -> {
switch (item.getDataType()) {
case "bigint":
item.setJavaType("Long");
break;
case "varchar":
item.setJavaType("String");
break;
case "tinyint":
item.setJavaType("Integer");
break;
case "datetime":
item.setJavaType("LocalDateTime");
break;
case "bit":
item.setJavaType("Boolean");
case "int":
item.setJavaType("Integer");
break;
default:
throw new NullPointerException("请维护类型:" + item.getDataType());
}
});
}
}
在父包
下创建包enums
,存放枚举,创建枚举GeneratorEnum
和MethodEnum
,内容如下
public enum GeneratorEnum {
ENTITY,
ENUM,
INFO,
PAGE_REQUEST,
CREATE_REQUEST,
UPDATE_REQUEST,
MAPPER,
MAPPER_XML,
SERVICE,
SERVICE_IMPL,
CONTROLLER,
}
public enum MethodEnum {
PAGE, // 分页查询
GET, // 详情查询
DELETE, // 删除
}
在父包
下创建包mapper
,创建访问数据库的接口DatabaseMapper
,内容如下
@Mapper
public interface DatabaseMapper {
Table getTableInfo(@Param("databaseName") String databaseName, @Param("tableName") String tableName);
List<Column> getColumnInfo(@Param("databaseName") String databaseName, @Param("tableName") String tableName);
}
在resource
包下创建mapper
包,并在mapper
包下创建XML文件DatabaseMapper.xml
用来编写SQL,内容如下:
<?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="com.shine.generator.mapper.DatabaseMapper">
<select id="getTableInfo" resultType="com.shine.generator.entity.Table">
select table_name, table_comment as comment
from information_schema.tables
where table_schema = #{databaseName}
and table_name = #{tableName}
</select>
<select id="getColumnInfo" resultType="com.shine.generator.entity.Column">
select column_name, data_type, column_comment as comment
from information_schema.columns
where table_schema = #{databaseName}
and table_name = #{tableName}
</select>
</mapper>
在父包
下创建包handler
,创建接口和抽象类
,以及生成实体类的类,内容如下:
public interface GeneratorHandler {
GeneratorEnum getEnum();
List<String> getIgnoreColumnList();
void handleTable(Table table);
void handler(Table table) throws Exception;
}
@Data
@Slf4j
@Component
public abstract class AbstractGeneratorHandler implements GeneratorHandler {
@Autowired
private GeneratorProperties properties;
@Override
public List<String> getIgnoreColumnList() {
return Collections.emptyList();
}
@Override
public void handleTable(Table table) {
}
}
@Slf4j
@Component
public class EntityHandler extends AbstractGeneratorHandler implements GeneratorHandler {
private final GeneratorEnum generatorEnum = GeneratorEnum.ENTITY;
@Override
public GeneratorEnum getEnum() {
return this.generatorEnum;
}
@Override
public void handleTable(Table table) {
}
@Override
public void handler(Table table) throws Exception {
log.info("开始生成{}实体类", table.getEntityName());
Configuration cfg = new Configuration(Configuration.VERSION_2_3_31);
cfg.setClassForTemplateLoading(GeneratorApplication.class, "/templates");
cfg.setDefaultEncoding("UTF-8");
// 2. 加载模板
Template template = cfg.getTemplate("entity.ftl");
// 4. 生成文件
File output = new File(super.getProperties().getPackageConfig().getModalName() + "/entity/" + table.getClassName() + ".java");
File parentDir = output.getParentFile();
if (!parentDir.exists()) {
if (parentDir.mkdirs()) {
log.info("成功创建目录:{}", parentDir.getAbsolutePath());
} else {
throw new IOException("无法创建目录:" + parentDir.getAbsolutePath());
}
}
try (FileWriter writer = new FileWriter(output)) {
template.process(table, writer);
}
log.info("生成{}实体类成功", table.getEntityName());
}
}
在resource
包下创建包templates
用来存放模板,创建模板entity.ftl
内容如下
package ${packagePath}.${moduleName}.entity;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
<#assign hasDate = columnList?filter(item -> item.javaType?contains("LocalDateTime"))>
<#if (hasDate?size > 0)>
import java.time.LocalDateTime;
</#if>
@Data
@TableName(value = "${tableName}")
public class ${className} {
<#-- 循环字段生成代码 -->
<#list columnList as column>
/**
* ${column.comment!""}
*/
@TableField(value = "${column.columnName}")
private ${column.javaType} ${column.fieldName};
</#list>
}
在父包
下创建包entrance
,创建执行类GeneratorEntrance
,内容如下
@Slf4j
@Component
public class GeneratorEntrance {
// private final List<GeneratorHandler> handlerList = new ArrayList<>();
@Autowired
private GeneratorProperties properties;
@Autowired
private List<GeneratorHandler> handlerList;
@Autowired
private DatabaseMapper databaseMapper;
@Autowired
private TypeConvert typeConvert;
private List<Table> tableList = new ArrayList<>();
@PostConstruct
public void generator() throws Exception {
ready();
handler();
}
/**
* 准备
*/
public void ready() {
List<GeneratorEnum> enumList = properties.getGenerator().getCode();
handlerList = handlerList.stream().filter(item -> enumList.contains(item.getEnum())).collect(Collectors.toList());
log.info("查询表对象");
List<String> tableNameList = properties.getGenerator().getTableName();
for (String tableName : tableNameList) {
// 查询数据库
Table table = databaseMapper.getTableInfo(properties.getGenerator().getDatabase(), tableName);
// 包和模块信息
table.setPackagePath(properties.getPackageConfig().getBasePackage());
table.setModuleName(properties.getPackageConfig().getModalName());
this.tableNameConvert(table);
// 转换表
List<Column> columnList = databaseMapper.getColumnInfo(properties.getGenerator().getDatabase(), tableName);
this.columnNameConvert(columnList);
this.typeConvert(columnList);
table.setColumnList(columnList);
LocalDateTime now = LocalDateTime.now();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm");
String date = formatter.format(now);
table.setGeneratorDate(date);
tableList.add(table);
}
log.info("表对象封装完成");
}
/**
* 处理
*
* @throws Exception
*/
public void handler() throws Exception {
for (GeneratorHandler handler : this.handlerList) {
for (Table table : tableList) {
Table newTable = new Table();
BeanUtils.copyProperties(table, newTable);
handler.handleTable(newTable);
handler.handler(newTable);
}
}
}
/**
* 列类型转换
*
* @param columnList
*/
protected void typeConvert(List<Column> columnList) {
this.typeConvert.convert(columnList);
}
/**
* 表名处理
*
* @param table
*/
protected void tableNameConvert(Table table) {
String tableName = table.getTableName();
String tablePrefix = properties.getGenerator().getTablePrefix();
if (StringUtils.isNotBlank(tablePrefix)) {
tableName = tableName.substring(tablePrefix.length());
}
String className = tableName.substring(0, 1).toUpperCase() + tableName.substring(1);
String entityName = this.toCamelCase(className);
table.setLowercaseClassName(entityName.substring(0, 1).toLowerCase() + entityName.substring(1));
table.setEntityName(entityName);
table.setClassName(entityName);
}
/**
* 列名处理
*
* @param columnList
*/
protected void columnNameConvert(List<Column> columnList) {
columnList.forEach(item -> {
String fieldName = this.nameConvert(item.getColumnName());
item.setFieldName(fieldName);
});
}
private String nameConvert(String name) {
if (name == null || name.isEmpty()) {
throw new NullPointerException();
}
// 使用下划线分割字符串
String[] parts = name.split("_");
StringBuilder camelCaseString = new StringBuilder();
// 遍历每一部分
for (int i = 0; i < parts.length; i++) {
if (i == 0) {
// 第一个单词全部小写
camelCaseString.append(parts[i].toLowerCase());
} else {
// 其它单词首字母大写
camelCaseString.append(parts[i].substring(0, 1).toUpperCase());
camelCaseString.append(parts[i].substring(1).toLowerCase());
}
}
return camelCaseString.toString();
}
public String toCamelCase(String str) {
if (str == null || str.isEmpty()) {
return str;
}
StringBuilder result = new StringBuilder();
boolean toUpperCase = false;
for (int i = 0; i < str.length(); i++) {
char c = str.charAt(i);
if (c == '_') {
toUpperCase = true; // 遇到下划线,标记下一个字符需要大写
} else {
if (toUpperCase) {
result.append(Character.toUpperCase(c)); // 转为大写
toUpperCase = false;
} else {
result.append(c); // 保留原样
}
}
}
return result.toString();
}
}
到这里,已经可以生成实体类了(要修改配置),下节结束之后我会分享代码仓库的,我准备将这个模块单独创建一个仓库,后面也可以拿来使用
我承认这一块我的代码写的比较辣鸡,但是这里的只需要可以正常生成代码就行,所以我没有很在意,我们下期见~
标签:06,String,重复,代码,List,private,生成,table,public From: https://blog.csdn.net/m0_56441750/article/details/145228394