⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 blog.csdn.net/qq_51076413/article/details/135394070 「.陌路」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注微信公众号:【芋道源码】有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言都将得到认真回复。甚至不知道如何读源码也可以请教噢。
  4. 新的源码解析文章实时收到通知。每周更新一篇左右。
  5. 认真的源码交流微信群。

0 自定义代码生成器

代码基于SpringBoot3、Vue3、highlight实现自定义代码生成功能

SpringBoot3.x、MySQL8、MyBatisPlus3.5.x、velocity2.x、SpringSecurity6.x、Vue3、TypeScript、highlight

demo所需要的依赖及其对应版本号

pom

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>cn.molu</groupId>
<artifactId>mgzyf-api</artifactId>
<version>2.4.1</version>
<description>Java 17 + SpringBoot3 + SpringSecurity6 </description>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.1.5</version> <!-- lookup parent from repository -->
<relativePath/>
</parent>

<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

<hutool.version>5.8.15</hutool.version>
<mysql.version>8.0.28</mysql.version>
<druid.version>1.2.16</druid.version>
<mybatis-plus.version>3.5.3.1</mybatis-plus.version>
<mapstruct.version>1.5.3.Final</mapstruct.version>
<lombok-mapstruct-binding.version>0.2.0</lombok-mapstruct-binding.version>
</properties>

<dependencies>
<!-- velocity -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.3</version>
</dependency>

<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<!--编译测试环境,不打包在lib-->
<scope>provided</scope>
</dependency>

<!-- 允许使用Lombok的Java Bean类中使用MapStruct注解 (Lombok 1.18.20+) -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-mapstruct-binding</artifactId>
<version>${lombok-mapstruct-binding.version}</version>
<scope>provided</scope>
</dependency>

<!-- hutool工具包 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>${hutool.version}</version>
</dependency>

<!-- web支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- security安全支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>

<!-- 连接池 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>

<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>

<!-- 阿里druid工具包 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>${druid.version}</version>
</dependency>

<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.40</version>
</dependency>

<!-- mybatis-plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>

<!-- 参数验证 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>

<!-- 自定义配置类支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies>

<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

</project>

1 代码生成实现步骤

配置文件

这里是最基础的MySQL的配置信息

application

server:
port: 8989

spring:
jackson:
# 默认序列化时间格式
date-format: yyyy-MM-dd HH:mm:ss
# 默认序列化时区
time-zone: GMT+8
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
druid:
master:
url: jdbc:mysql://127.0.0.1:3306/test01?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&autoReconnect=true&allowMultiQueries=true
username: xxx
password: 123
driver-class-name: com.mysql.cj.jdbc.Driver
initial-size: 15
min-idle: 15
max-active: 200
max-wait: 60000
time-between-eviction-runs-millis: 60000
min-evictable-idle-time-millis: 300000
validation-query: ""
test-while-idle: true
test-on-borrow: false
test-on-return: false
pool-prepared-statements: false
connection-properties: false

# 配置生成代码的数据库
cn:
molu:
generate:
database: test01

1.1、代码生成器源码目录

这里是代码生成器的源码目录结构,下面会逐一展示这3个类

1.2、自定义模板目录

这里是根据我的项目需要,自定义的代码生成器模板,下面会以Service为例解释

1.3、代码生成器源码

1.3.1、API接口层

提供数据到Vue页面的API接口,主要功能是将模板解析为代码返回给前台

import cn.molu.system.common.result.Result;
import cn.molu.system.generate.mapper.ColumnDetailMapper;
import cn.molu.system.generate.vo.ColumnDetailVo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.exception.MethodInvocationException;
import org.apache.velocity.exception.ParseErrorException;
import org.apache.velocity.exception.ResourceNotFoundException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
import java.io.StringWriter;
import java.util.List;
import java.util.Map;
import java.util.Properties;

/**
* @author 陌路
* @apiNote 数据库表细信息
* @date 2023/12/31 13:16
* @tool Created by IntelliJ IDEA
*/
@Slf4j
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/v1/gen")
public class ColumnDetailController {

@Value("${cn.molu.generate.database}")
private String database; // 这里是数据库的名字,我的数据库是 test01
private final ColumnDetailMapper columnDetailMapper;

/**
* 获取所有表信息数据
*
* @return List<ColumnDetailVo>
*/
@GetMapping("/getAllTables")
@Cacheable(value = "gen:allTableDetails", key = "#root.methodName")
public Result<List<ColumnDetailVo>> getAllTables() {
List<ColumnDetailVo> columnDetailVoList = columnDetailMapper.getColumnDetailMapVo(database);
return Result.success(columnDetailVoList);
}

/**
* 获取所有表中字段信息数据
*
* @param tableName 表名
* @return List<ColumnDetailVo>
*/
@GetMapping("/getTableInfo/{tableName}")
public Result<String> getTableInfo(@PathVariable String tableName,
@RequestParam(value = "delPrefix", required = false) String delPrefix,
@RequestParam(value = "packageName", required = false) String packageName,
@RequestParam(value = "type") String type
) {
List<ColumnDetailVo> columnDetailVoList = columnDetailMapper.getColumnDetailMapVoByTableName(database, tableName);
try (StringWriter writer = new StringWriter()) {
// 初始化Velocity引擎
Properties p = new Properties();
// 加载classpath目录下的vm文件
p.setProperty("resource.loader.file.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
// 定义字符集
p.setProperty(Velocity.INPUT_ENCODING, "UTF-8");
// 初始化Velocity引擎,指定配置Properties
Velocity.init(p);
// 获取模板
Template template = Velocity.getTemplate("vm/" + type + ".vm", "UTF-8");
ColumnDetailVo detailVo = new ColumnDetailVo();
Map<String, Object> map = detailVo.listToMap(columnDetailVoList, delPrefix); // 去除前缀
map.put("packageName", packageName); // 文件所在包
// 创建Velocity上下文
VelocityContext context = new VelocityContext();
// 设置模板中的变量
context.put("map", map);
// 将模板与上下文数据进行合并
template.merge(context, writer);
// 输出结果
String writerString = writer.toString();
return Result.success(writerString);
} catch (IOException | ResourceNotFoundException | ParseErrorException | MethodInvocationException e) {
log.error("报错了:" + e.getMessage(), e);
}
return Result.failed();
}
}

1.3.2、Mapper

查询数据库,获取表字段信息,以便后面生成代码使用

import cn.molu.system.generate.vo.ColumnDetailVo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;

/**
* @author 陌路
* @apiNote 数据库表细信息
* @date 2023/12/31 13:14
* @tool Created by IntelliJ IDEA
*/
@Mapper
public interface ColumnDetailMapper {

/**
* 获取所有表信息数据
*
* @return List<ColumnDetailVo>
*/
public List<ColumnDetailVo> getColumnDetailMapVo(@Param("database") String database);

/**
* 获取所有表中字段信息数据
*
* @param tableName 表名
* @return List<ColumnDetailVo>
*/
public List<ColumnDetailVo> getColumnDetailMapVoByTableName(@Param("database") String database, @Param("tableName") String tableName);
}

1.3.3、对应Mapper的xml

根据application中指定的数据库名,获取数据库中所有的表名以及表中的字段名

<?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="cn.molu.system.generate.mapper.ColumnDetailMapper">

<resultMap type="cn.molu.system.generate.vo.ColumnDetailVo" id="ColumnDetailMap">
<result property="tableName" column="table_name" jdbcType="VARCHAR"/>
<result property="tableComment" column="table_comment" jdbcType="VARCHAR"/>
<result property="engine" column="engine" jdbcType="VARCHAR"/>
<result property="tableCollation" column="table_collation" jdbcType="VARCHAR"/>
<result property="tableRows" column="table_rows" jdbcType="VARCHAR"/>
<result property="createTime" column="create_time" jdbcType="VARCHAR"/>
</resultMap>

<!-- 获取所有表信息数据 -->
<select id="getColumnDetailMapVo" resultMap="ColumnDetailMap">
select table_name, <!-- 表名 -->
table_comment, <!-- 表名注释 -->
engine, <!-- 表数据引擎 -->
table_collation, <!-- 表字符集 -->
table_rows, <!-- 表中数据条数 -->
create_time <!-- 表创建时间 -->
from information_schema.tables
where upper(table_schema) = upper(#{database})
</select>

<!-- 获取所有表中字段信息数据 -->
<select id="getColumnDetailMapVoByTableName" parameterType="string" resultMap="ColumnDetailMap">
SELECT TABLE_SCHEMA, <!-- 数据库名 -->
TABLE_NAME, <!-- 表名 -->
(select b.table_comment
from information_schema.tables b
where upper(table_schema) = upper(#{database})
and upper(table_name) = upper(#{tableName})
limit 1) as TABLE_COMMENT, <!-- 表名注释 -->
COLUMN_NAME, <!-- 字段名 -->
COLUMN_DEFAULT, <!-- 默认值 -->
IS_NULLABLE, <!-- 是否可为空:YES、NO -->
DATA_TYPE, <!-- 数据类型:int、varchar... -->
COLUMN_TYPE, <!-- 字段类型:int、varchar(30)... -->
COLUMN_KEY, <!-- 是否主键:PRI -->
EXTRA, <!-- 是否自增:auto_increment(自增) -->
COLUMN_COMMENT <!-- 字段注释 -->
FROM information_schema.columns a
WHERE upper(table_schema) = upper(#{database})
AND upper(table_name) = upper(#{tableName})
</select>
</mapper>

1.3.4、各字段想起类

对应表中字段和字段信息的实体类

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.experimental.Accessors;
import org.springframework.format.annotation.DateTimeFormat;
import java.io.Serial;
import java.io.Serializable;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;

/**
* @author 陌路
* @apiNote 数据库表细信息
* @date 2023/12/31 13:21
* @tool Created by IntelliJ IDEA
*/
@Data
@ToString
@NoArgsConstructor
@EqualsAndHashCode
@Accessors(chain = true)
@JsonInclude(value = JsonInclude.Include.NON_NULL, content = JsonInclude.Include.NON_EMPTY)
public class ColumnDetailVo implements Serializable {
@Serial
private static final long serialVersionUID = 9196390045041955368L;
// ====================查询所有表信息====================
/**
* 表名
*/
private String tableName;
/**
* 表名注释
*/
private String tableComment;
/**
* 表数据引擎
*/
private String engine;
/**
* 表字符集
*/
private String tableCollation;
/**
* 表中数据条数
*/
private String tableRows;
/**
* 表创建时间
*/
@JsonFormat(pattern = "YYYY-MM-DD HH:mm:ss")
@DateTimeFormat(pattern = "YYYY-MM-DD HH:mm:ss")
private String createTime;

// ====================查询指定表中的字段信息====================
/**
* 数据库名
*/
private String tableSchema;
/**
* 字段名
*/
private String columnName;
/**
* 默认值
*/
private String columnDefault;
/**
* 是否可为空:YES、NO
*/
private String isNullable;
/**
* 数据类型:int、varchar...
*/
private String dataType;
/**
* 字段类型:int、varchar(30)...
*/
private String columnType;
/**
* 是否主键:PRI
*/
private String columnKey;
/**
* 是否自增:auto_increment(自增)
*/
private String extra;
/**
* 字段注释
*/
private String columnComment;

/**
* 是否主键
*
* @param columnKey 字段键
* @return 是否主键
*/
public boolean isPrimaryKey(String columnKey) {
return StrUtil.equalsIgnoreCase(columnKey, "PRI");
}

/**
* 获取主键字段名
*
* @param list 字段列表
* @return 主键字段名
*/
public String getPrimaryKey(List<ColumnDetailVo> list) {
return Optional.ofNullable(list)
.orElseGet(ArrayList::new)
.stream()
.filter(item -> isPrimaryKey(item.columnKey))
.findFirst()
.orElseGet(ColumnDetailVo::new).columnName;
}

/**
* 列表转Map
*
* @param list 列表
* @param replacePrefix 替换前缀
* @return Map
*/
public Map<String, Object> listToMap(List<ColumnDetailVo> list, String replacePrefix) {
if (Objects.isNull(list) || list.isEmpty()) return null;
Map<String, Object> map = new HashMap<>(2);
ColumnDetailVo detailVo = list.get(0);
String voTableName = detailVo.getTableName().toLowerCase();
String subTableName = voTableName.replace(replacePrefix, "");
String className = toUpperFirst(subTableName);
map.put("tableName", voTableName); // 表名
map.put("tableComment", detailVo.getTableComment()); // 表名注释
map.put("className", StrUtil.upperFirst(className)); // Java类名
map.put("subClassName", className); // Java类名
map.put("path", voTableName.replace("_", "/")); // 生成路径
map.put("prem", voTableName.replace("_", ":")); // 权限标识
map.put("currTime", DateUtil.format(new Date(), DatePattern.NORM_DATETIME_PATTERN)); // 日期时间
AtomicReference<String> pk = new AtomicReference<>();
AtomicReference<String> pkType = new AtomicReference<>();
AtomicReference<String> getterPk = new AtomicReference<>();
AtomicReference<String> pkColumn = new AtomicReference<>();
List<Map<String, String>> fields = new ArrayList<>(2);
List<Map<String, String>> otherColumn = new ArrayList<>(2);
list.forEach(vo -> {
Map<String, String> field = new HashMap<>(2);
field.put("columnName", vo.getColumnName()); // 字段名
field.put("javaField", toUpperFirst(vo.getColumnName())); // 字段对应的Java字段
field.put("columnComment", vo.getColumnComment()); // 字段注释
String javaType = getJavaType(vo.getDataType());
field.put("javaType", javaType); // java字段类型
field.put("jdbcType", vo.getDataType().toUpperCase());
field.put("jdbcTypeXml", getJdbcTypeXml(vo.getDataType().toLowerCase()));
field.put("type", getType(vo.getDataType()));
field.put("getter", StrUtil.upperFirst(toUpperFirst(vo.getColumnName())));
if (isPrimaryKey(vo.getColumnKey())) {
if (StrUtil.equalsIgnoreCase(javaType, "Integer")) {
javaType = "Long";
}
field.put("javaType", javaType); // java字段类型
field.put("pkColumn", vo.getColumnName()); // 主键
pk.set(toUpperFirst(vo.getColumnName()));
pkColumn.set(vo.getColumnName());
pkType.set(javaType);
getterPk.set(StrUtil.upperFirst(toUpperFirst(vo.getColumnName())));
} else {
otherColumn.add(field);
}
fields.add(field);
});
map.put("columns", fields);
map.put("otherColumn", otherColumn);
map.put("pk", pk);
map.put("pkColumn", pkColumn);
map.put("pkType", pkType);
map.put("getterPk", getterPk);
return map;
}

/**
* @title 获取字段类型
* @author 陌路
* @date 2023/12/31 21:01
*/
private String getJdbcTypeXml(String dataType) {
return switch (dataType) {
case "int", "tinyint" -> "INTEGER";
case "char" -> "CHAR";
case "mediumint" -> "MEDIUMINT";
case "bigint" -> "BIGINT";
case "float" -> "FLOAT";
case "double" -> "DOUBLE";
case "bit" -> "BIT";
case "datetime", "date", "time", "timestamp" -> "TIMESTAMP";
default -> "VARCHAR";
};
}

/**
* @title 获取字段类型
* @author 陌路
* @date 2023/12/31 21:01
*/
private String getType(String dataType) {
String javaType = getJavaType(dataType);
String type = "java.lang." + dataType;
if (StrUtil.equals(javaType, "Date")) {
type = "java.util.Date";
}
return type;
}

/**
* @title 获取字段类型
* @author 陌路
* @date 2023/12/31 21:01
*/
private String getJavaType(String dataType) {
return switch (dataType) {
case "bigint" -> "Long";
case "datetime", "date", "time", "timestamp" -> "Date";
case "decimal", "double" -> "Double";
case "float" -> "Float";
case "int", "tinyint", "integer" -> "Integer";
default -> "String";
};
}

/**
* 首字母转大写
*
* @param field 字段
* @return 首字母大写
*/
public String toUpperFirst(String field) {
// 表名转驼峰命名
StringBuilder string = new StringBuilder();
if (StrUtil.isNotEmpty(field) && field.contains("_")) {
for (String str : field.split("_")) {
string.append(StrUtil.upperFirst(str));
}
} else {
string = new StringBuilder(StrUtil.upperFirst(field));
}
return StrUtil.lowerFirst(string.toString());
}
}

1.4、自定义模板

自定义的模板,通过模板生成所需要的代码

package $!{map.packageName}.service;

import $!{map.packageName}.entity.$!{map.className};
import com.baomidou.mybatisplus.core.metadata.IPage;
import $!{map.packageName}.params.$!{map.className}Params;
import java.lang.*;

/**
* @apiNote $!{map.tableComment}($!{map.className})表服务接口
* @author 陌路
* @date $!{map.currTime}
* @tool Created by IntelliJ IDEA
*/
public interface $!{map.className}Service /* extends IService<$!{map.className}> */{

/**
* 通过ID查询单条数据
*
* @author 陌路
* @date $!{map.currTime}
* @param $!{map.pk} 主键
* @return $!{map.subClassName} 实例对象
*/
$!{map.className} queryById($!{map.pkType} $!{map.pk});

/**
* 分页查询
*
* @author 陌路
* @date $!{map.currTime}
* @param $!{map.subClassName}Params 筛选条件
* @return IPage<$!{map.className}> 查询结果
*/
IPage<$!{map.className}> queryByPage($!{map.className}Params $!{map.subClassName}Params);

/**
* 新增数据
*
* @author 陌路
* @date $!{map.currTime}
* @param $!{map.subClassName} 实例对象
* @return $!{map.subClassName} 实例对象
*/
$!{map.className} insert($!{map.className} $!{map.subClassName}));

/**
* 修改数据
*
* @author 陌路
* @date $!{map.currTime}
* @param $!{map.subClassName} 实例对象
* @return $!{map.subClassName} 实例对象
*/
$!{map.className} update($!{map.className} $!{map.subClassName}));

/**
* 通过主键作废(逻辑删除)
*
* @author 陌路
* @date $!{map.currTime}
* @param ids 主键
* @return boolean 是否成功
*/
boolean deleteByIds(String ids);

/**
* 通过主键删除(物理删除)
*
* @author 陌路
* @date $!{map.currTime}
* @param ids 主键
* @return boolean 是否成功
*/
boolean removeByIds(String ids);
}

1.5、页面预览代码

后台生成代码返回给vue页面,vue渲染,并使用语法高亮展示完整代码

<script setup lang="ts">
defineOptions({
name: "Code",
inheritAttrs: false,
});

import { ColumnDetailQuery, ColumnDetailVo } from "@/api/generate/types";
import { getAllTables, getTableInfo } from "@/api/generate";
import { ref, onMounted, reactive } from "vue";

import "highlight.js/styles/atom-one-light.css";
import "highlight.js/lib/common";
import hljs from "highlight.js";

const queryParams = reactive<ColumnDetailQuery>({});
const queryFormRef = ref(ElForm);
const dialog = reactive<DialogOption>({
visible: false,
title: "",
});
// 选择表格行的数据ID
const loading = ref(false); // 加载状态
const columnDetailVoList = ref<ColumnDetailVo[]>([]); // 记录列表数据
const dataType = ref(0); // 数据类型
const params = ref({
tableName: "", // 表名
delPrefix: "", // 删除前缀
packageName: "cn.molu.system", // 所在包
type: "java/entity.java", // 数据类型
});
const result = ref({});
const codeCache = new Map();
const tableName = ref("");
const preview = ref({
activeName: "entity",
data: [],
title: "",
open: false,
code: null,
vmName: "entity.java",
});
// 这里可以从后台获取,我这里为了直观的展示就写死了这几种
const tabPaneData = reactive<any[]>([
{
label: "entity.java",
name: "entity",
path: "java/entity.java",
},
{
label: "controller.java",
name: "controller",
path: "java/controller.java",
},
{
label: "service.java",
name: "service",
path: "java/service.java",
},
{
label: "serviceImpl.java",
name: "scopeImpl",
path: "java/serviceImpl.java",
},
{
label: "mapper.java",
name: "mapper",
path: "java/mapper.java",
},
{
label: "mapperXml.xml",
name: "mapperXml",
path: "xml/mapper.xml",
},
{
label: "params.java",
name: "params",
path: "java/params.java",
},
{
label: "api.ts",
name: "api",
path: "vue3/api.ts",
},
{
label: "page.vue",
name: "page",
path: "vue3/page.vue",
},
{
label: "types.ts",
name: "types",
path: "vue3/types.ts",
},
{
label: "sql.sql",
name: "sql",
path: "sql/sql.sql",
},
]);

/**
* 查询按钮点击事件
*/
const handleQuery = () => {
getAllTables()
.then(({ data }) => {
columnDetailVoList.value = data;
})
.finally(() => {
loading.value = false;
});
};

/**
* 重置按钮点击事件
*/
const resetQuery = () => {
queryFormRef.value.resetFields();
handleQuery();
};
/** 关闭弹窗 */
const closeDialog = () => {
dialog.visible = false;
};

/**
* 显示弹框
*/
const openDialog = (row: ColumnDetailVo) => {
dialog.visible = true;
dialog.title = "代码预览";
console.log(row);
params.value.tableName = row.tableName || "";
if (!params.value.delPrefix && row.tableName?.includes("_")) {
params.value.delPrefix = row.tableName?.split("_")[0] + "_";
}
tableName.value = row.tableName || "";
generateCode();
};

/**
* 选择列表
*/
const changeSelect = (value: string) => {
console.log(value);
params.value.tableName = value;
if (!params.value.delPrefix && value?.includes("_")) {
params.value.delPrefix = value?.split("_")[0] + "_";
}
tableName.value = value || "";
generateCode();
};

/**
* 生成代码
*/
const generateCode = () => {
let obj = codeCache.get(tableName.value) || {};
let activeName = preview.value.activeName;
if (obj && obj[activeName]) {
let code = obj[activeName] || "";
result.value = code;
return;
}
getTableInfo(tableName.value, params.value).then((response) => {
const data = response.data;
preview.value.code = data;
const vmName = preview.value.vmName || "";
let language = vmName.substring(vmName.indexOf(".") + 1, vmName.length).toLowerCase();
language = ["vue", "html", "xml"].includes(language) ? "html" : language;
language = language === 'js' ? "javascript": language;
result.value = hljs.highlight(data || "", {
language: language,
ignoreIllegals: true,
}).value;
obj[activeName] = result.value || "";
codeCache.set(tableName.value, obj);
return result.value || "&nbsp;";
});
};

/**
* tab切换事件
*/
const tabChange = (tabPaneName: any) => {
console.log(tabPaneName);
preview.value.activeName = tabPaneName;
const tabPane = tabPaneData.find((item) => item.name == tabPaneName);
console.log(tabPane);
preview.value.vmName = tabPane.label;
params.value.type = tabPane.path;
generateCode();
};
/**
* 复制代码
*/
const clipboardSuccess = () => {
ElMessage.success("复制成功");
};

/**
* 选择页码,分页查询
*/
const updateCurrentPage = (page?: number) => {
queryParams.page = page;
handleQuery();
};

/**
* 页面加载完成后执行
*/
onMounted(() => {
handleQuery();
});
</script>

<template>
<div class="app-container">
<div class="search-container">
<el-form ref="queryFormRef" :model="params" :inline="true">
<!-- 表名 -->
<el-form-item label="表名" prop="tableName">
<el-input v-model="params.tableName" placeholder="表名" disabled />
</el-form-item>
<!-- 删除前缀 -->
<el-form-item label="删除前缀" prop="delPrefix" v-if="false">
<el-input
v-model="params.delPrefix"
placeholder="删除前缀"
clearable
/>
</el-form-item>
<!-- 所在包 -->
<el-form-item label="所在包" prop="packageName" v-if="false">
<el-input
v-model="params.packageName"
placeholder="所在包"
clearable
/>
</el-form-item>
<el-form-item>
<el-button class="filter-item" type="primary" @click="handleQuery">
<i-ep-search />
搜索
</el-button>
<el-button @click="resetQuery"> <i-ep-refresh />重置 </el-button>
</el-form-item>
</el-form>
</div>

<el-card shadow="never" class="table-container">
<el-scrollbar style="height: 400px">
<el-table
v-loading="loading"
:data="columnDetailVoList"
row-key="id"
default-expand-all
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
>
<!-- 复选款 -->
<el-table-column type="selection" width="55" align="center" />
<!-- 序号 -->
<el-table-column
type="index"
width="55"
align="center"
label="序号"
/>
<!-- 数据库名 -->
<el-table-column
label="数据库名"
align="center"
prop="tableSchema"
v-if="dataType == 1"
>
<template #default="scope">
{{ scope.row.tableSchema }}
</template>
</el-table-column>
<!-- 表名 -->
<el-table-column label="表名" align="center" prop="tableName">
<template #default="scope">
{{ scope.row.tableName }}
</template>
</el-table-column>
<!-- 表注释 -->
<el-table-column
label="表注释"
align="center"
prop="tableComment"
v-if="dataType == 0"
>
<template #default="scope">
{{ scope.row.tableComment }}
</template>
</el-table-column>
<!-- 表数据引擎 -->
<el-table-column
label="表引擎"
align="center"
prop="engine"
v-if="dataType == 0"
>
<template #default="scope">
{{ scope.row.engine }}
</template>
</el-table-column>
<!-- 表字符集 -->
<el-table-column
label="字符集"
align="center"
prop="tableCollation"
v-if="dataType == 0"
>
<template #default="scope">
{{ scope.row.tableCollation }}
</template>
</el-table-column>
<!-- 表中数据条数 -->
<el-table-column
label="数据量"
align="center"
prop="tableRows"
v-if="dataType == 0"
>
<template #default="scope">
{{ scope.row.tableRows }}
</template>
</el-table-column>
<!-- 表创建时间 -->
<el-table-column
label="创建时间"
align="center"
prop="createTime"
v-if="dataType == 0"
>
<template #default="scope">
{{ scope.row.createTime }}
</template>
</el-table-column>

<!-- 字段名 -->
<el-table-column
label="字段名"
align="center"
prop="columnName"
v-if="dataType == 1"
>
<template #default="scope">
{{ scope.row.columnName }}
</template>
</el-table-column>
<!-- 默认值 -->
<el-table-column
label="默认值"
align="center"
prop="columnDefault"
v-if="dataType == 1"
>
<template #default="scope">
{{ scope.row.columnDefault }}
</template>
</el-table-column>
<!-- 是否可为空:YES、NO -->
<el-table-column
label="可为空"
align="center"
prop="isNullable"
v-if="dataType == 1"
>
<template #default="scope">
{{ scope.row.isNullable }}
</template>
</el-table-column>
<!-- 数据类型:int、varchar... -->
<el-table-column
label="数据类型"
align="center"
prop="dataType"
v-if="dataType == 1"
>
<template #default="scope">
{{ scope.row.dataType }}
</template>
</el-table-column>
<!-- 字段类型:int、varchar(30)... -->
<el-table-column
label="字段类型"
align="center"
prop="columnType"
v-if="dataType == 1"
>
<template #default="scope">
{{ scope.row.columnType }}
</template>
</el-table-column>
<!-- 是否主键:PRI -->
<el-table-column
label="是否主键"
align="center"
prop="columnKey"
v-if="dataType == 1"
>
<template #default="scope">
{{ scope.row.columnKey }}
</template>
</el-table-column>
<!-- 是否自增:auto_increment(自增) -->
<el-table-column
label="是否自增"
align="center"
prop="extra"
v-if="dataType == 1"
>
<template #default="scope">
{{ scope.row.extra }}
</template>
</el-table-column>
<!-- 字段注释 -->
<el-table-column
label="字段注释"
align="center"
prop="columnComment"
v-if="dataType == 1"
>
<template #default="scope">
{{ scope.row.columnComment }}
</template>
</el-table-column>
<!-- 操作 -->
<el-table-column
label="操作"
fixed="right"
align="left"
width="200"
v-if="dataType == 0"
>
<template #default="scope">
<el-button
type="primary"
link
size="small"
@click.stop="openDialog(scope.row)"
>
<i-ep-view />预览
</el-button>
</template>
</el-table-column>
</el-table>
</el-scrollbar>
<div
style="
margin: 15px auto;
display: flex;
align-items: center;
justify-content: center;
"
>
<!-- 这里是分页插件 -->
<!-- <el-pagination
background
layout="prev, pager, next"
:total="queryParams.total"
:default-page-size="10"
v-model:page-size="queryParams.size"
v-model:current-page="queryParams.page"
@update:current-page="updateCurrentPage"
/> -->
</div>
</el-card>

<!-- 预览弹窗 -->
<el-dialog
v-model="dialog.visible"
:title="dialog.title"
destroy-on-close
append-to-body
align-center
lock-scroll
width="80%"
@close="closeDialog"
top="3vh"
>
<el-form ref="queryFormRef" :model="params" :inline="true">
<!-- 表名 -->
<el-form-item label="表名" prop="tableName">
<el-select
v-model="params.tableName"
placeholder="表名"
@change="changeSelect"
>
<el-option
v-for="(item, i) in columnDetailVoList"
:key="i"
:label="item.tableName"
:value="item.tableName"
/>
</el-select>
</el-form-item>
<!-- 删除前缀 -->
<el-form-item label="删除前缀" prop="delPrefix">
<el-input v-model="params.delPrefix" placeholder="删除前缀" />
</el-form-item>
<!-- 所在包 -->
<el-form-item label="所在包" prop="packageName">
<el-input v-model="params.packageName" placeholder="所在包" />
</el-form-item>
<el-form-item>
<el-button class="filter-item" type="primary" @click="generateCode">
生成
</el-button>
</el-form-item>
</el-form>

<el-tabs
v-model="preview.activeName"
class="demo-tabs"
@tab-change="tabChange"
>
<el-scrollbar max-height="420px">
<el-tab-pane
v-for="(item, key) in tabPaneData"
:label="item.label"
:name="item.name"
:key="key"
>
<!-- <el-link :underline="false"
icon="el-icon-document-copy"
v-clipboard:copy="preview.code"
v-clipboard:success="clipboardSuccess"
style="float:right">复制</el-link> -->

<pre class="hljs-container">
<code class="hljs" v-html="result"></code>
</pre>
</el-tab-pane>
</el-scrollbar>
</el-tabs>
</el-dialog>
</div>
</template>

<style lang="scss" scoped>
/* 语法高亮 */
.hljs-container {
position: relative;
display: block;
// width: 600px;
padding: 30px 5px 2px;
overflow-x: hidden;
line-height: 20px;
text-align: left;
background: #21252b;
box-shadow: 0 10px 30px 0 rgb(0 0 0 / 40%);
}

/** 3个点 */
.hljs-container::before {
position: absolute;
top: 10px;
left: 15px;
width: 12px;
height: 12px;
overflow: visible;
font-weight: 700;
font-size: 16px;
line-height: 12px;
white-space: nowrap;
text-indent: 75px;
background-color: #fc625d;
border-radius: 16px;
box-shadow: 20px 0 #fdbc40, 40px 0 #35cd4b;
content: attr(codetype);
}

/** 滚动条 */
:deep(.hljs) {
overflow-x: auto;
color: #4887b8;
background-color: #21252b;
// background-color: #dddddd;
}

:deep(.hljs::-webkit-scrollbar) {
width: 12px !important;
height: 12px !important;
}

:deep(.hljs::-webkit-scrollbar-thumb) {
height: 30px !important;
background: #d1d8e6;
background-clip: content-box;
border: 2px solid transparent;
border-radius: 19px;
opacity: 0.8;
}

:deep(.hljs::-webkit-scrollbar-thumb:hover) {
background: #a5b3cf;
background-clip: content-box;
border: 2px solid transparent;
}

:deep(.hljs::-webkit-scrollbar-track-piece) {
width: 30px;
height: 30px;
background: #333;
}

::-webkit-scrollbar-button {
display: none;
}
</style>

1.6、代码生成器的使用

  • 选择需要生成代码的表
  • 根据个人需求是否去除表前缀
  • 生成的代码所在的包路径

点击生成即可生成代码,并可以直接复制使用

1.7、效果展示

1.7.1、菜单SQL

生成的菜单SQL语句,包含权限代码

1.7.2、生成的vue3页面

可以生成vue3的语法支持的前端页面

1.7.3、生成的xml文件

可以生成我们需要的mapper.xml文件

里面包含对数据库的增删改查的语句

1.7.4、生成的Service代码

这里可以生成对Service的代码实现类

1.7.5、生成的controller

生成对外提供的接口层,包含所需要的权限,接口问道的doc解释等

以上就是使用SpringBoot3、JDK17、Vue3、SpringSecurity6等实现的代码生成器了,其他页面就不一一展示了

优点:

  • 可扩展性高,可以根据自己不同的需求调整代码生成器以及生成逻辑
  • 可自定义生成内容,支持范围广
  • 可个自定义性化代码,按照自己喜欢的风格来自定义生成器
文章目录
  1. 1. 0 自定义代码生成器
  2. 2. 1 代码生成实现步骤
    1. 2.0.0.1. 配置文件
    2. 2.0.0.2. 1.1、代码生成器源码目录
    3. 2.0.0.3. 1.2、自定义模板目录
    4. 2.0.0.4. 1.3、代码生成器源码
    5. 2.0.0.5. 1.4、自定义模板
    6. 2.0.0.6. 1.5、页面预览代码
    7. 2.0.0.7. 1.6、代码生成器的使用
    8. 2.0.0.8. 1.7、效果展示