相关推荐recommended
手把手教你如何使用SpringBoot3打造一个个性化的代码生成器
作者:mmseoamin日期:2024-01-18

自定义代码生成器

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

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

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

pom



    4.0.0
    cn.molu
    mgzyf-api
    2.4.1
    Java 17 + SpringBoot3 + SpringSecurity6 
    
        org.springframework.boot
        spring-boot-starter-parent
        3.1.5 
        
    
    
        17
        17
        UTF-8
        5.8.15
        8.0.28
        1.2.16
        3.5.3.1
        1.5.3.Final
        0.2.0
    
    
        
        
            org.apache.velocity
            velocity-engine-core
            2.3
        
        
        
            org.projectlombok
            lombok
            
            provided
        
        
        
        
            org.projectlombok
            lombok-mapstruct-binding
            ${lombok-mapstruct-binding.version}
            provided
        
        
        
            cn.hutool
            hutool-all
            ${hutool.version}
        
		
        
            org.springframework.boot
            spring-boot-starter-web
        
		
		
        
            org.springframework.boot
            spring-boot-starter-security
        
		
        
            org.apache.commons
            commons-pool2
        
		
        
            mysql
            mysql-connector-java
            ${mysql.version}
        
        
        
            com.alibaba
            druid-spring-boot-starter
            ${druid.version}
        
        
        
            com.alibaba
            fastjson
            2.0.40
        
		
        
            com.baomidou
            mybatis-plus-boot-starter
            ${mybatis-plus.version}
        
		
        
            org.springframework.boot
            spring-boot-starter-validation
        
		
        
        	org.springframework.boot
        	spring-boot-configuration-processor
        	true
        
    
    
        ${project.artifactId}
        
            
                org.springframework.boot
                spring-boot-maven-plugin
            
        
    

代码生成实现步骤

配置文件

这里是最基础的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个类

手把手教你如何使用SpringBoot3打造一个个性化的代码生成器,在这里插入图片描述,第1张

1.2、自定义模板目录

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

手把手教你如何使用SpringBoot3打造一个个性化的代码生成器,在这里插入图片描述,第2张

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
     */
    @GetMapping("/getAllTables")
    @Cacheable(value = "gen:allTableDetails", key = "#root.methodName")
    public Result> getAllTables() {
        List columnDetailVoList = columnDetailMapper.getColumnDetailMapVo(database);
        return Result.success(columnDetailVoList);
    }
    /**
     * 获取所有表中字段信息数据
     *
     * @param tableName 表名
     * @return List
     */
    @GetMapping("/getTableInfo/{tableName}")
    public Result getTableInfo(@PathVariable String tableName,
                                       @RequestParam(value = "delPrefix", required = false) String delPrefix,
                                       @RequestParam(value = "packageName", required = false) String packageName,
                                       @RequestParam(value = "type") String type
    ) {
        List 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 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
     */
    public List getColumnDetailMapVo(@Param("database") String database);
    /**
     * 获取所有表中字段信息数据
     *
     * @param tableName 表名
     * @return List
     */
    public List getColumnDetailMapVoByTableName(@Param("database") String database, @Param("tableName") String tableName);
}
1.3.3、对应Mapper的xml

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




    
        
        
        
        
        
        
    
    
    
    
    

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 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 listToMap(List list, String replacePrefix) {
        if (Objects.isNull(list) || list.isEmpty()) return null;
        Map 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 pk = new AtomicReference<>();
        AtomicReference pkType = new AtomicReference<>();
        AtomicReference getterPk = new AtomicReference<>();
        AtomicReference pkColumn = new AtomicReference<>();
        List> fields = new ArrayList<>(2);
        List> otherColumn = new ArrayList<>(2);
        list.forEach(vo -> {
            Map 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渲染,并使用语法高亮展示完整代码



1.6、代码生成器的使用

1、选择需要生成代码的表

2、根据个人需求是否去除表前缀

3、生成的代码所在的包路径

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

手把手教你如何使用SpringBoot3打造一个个性化的代码生成器,在这里插入图片描述,第3张

1.7、效果展示

1.7.1、菜单SQL

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

手把手教你如何使用SpringBoot3打造一个个性化的代码生成器,在这里插入图片描述,第4张

1.7.2、生成的vue3页面

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

手把手教你如何使用SpringBoot3打造一个个性化的代码生成器,在这里插入图片描述,第5张

1.7.3、生成的xml文件

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

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

手把手教你如何使用SpringBoot3打造一个个性化的代码生成器,在这里插入图片描述,第6张

1.7.4、生成的Service代码

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

手把手教你如何使用SpringBoot3打造一个个性化的代码生成器,在这里插入图片描述,第7张

1.7.5、生成的controller

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

手把手教你如何使用SpringBoot3打造一个个性化的代码生成器,在这里插入图片描述,第8张

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

优点:

可扩展性高,可以根据自己不同的需求调整代码生成器以及生成逻辑

可自定义生成内容,支持范围广

可个自定义性化代码,按照自己喜欢的风格来自定义生成器