【实用技巧】模板代码快速生成

1  前言

我们在写代码的时候,尤其是持久层用到 Mybatis 的时候,可能会经常自己写 SQL,而对于一些有常用的有规律的 SQL,我们没必要自己写,可以整个工具来自己生成,比如批量新增的 insert values、新增或者更新的 insert on conflict update 等类似的语句,这节我就简单分享下我自己的生成小工具哈。

2  实现

我们要生成一些规律的 SQL, 那么我们想一下大概分两步或者三步吧,

(1)首先收集并整理我们的数据库表信息

(2)写模板

(3)执行生成

核心技术就是: freemarker + jdbc,jdbc用于收集数据库表信息并整理,然后写一个 freemarker 模板(要熟悉 freemarker 语法),最后执行 freemarker 渲染。

我们先看个想达到的效果,比如拿我的订单表为例:大概有100个左右的字段个数,我想写批量查询的 insert values,自己手写的话,那岂不是费劲,

而模板直接生成的话,模板如下:

生成的效果:

对于一些复杂的比如不仅仅是生成单表的一个语句,高级还有整个项目目录结构的整个数据库的表的所有增删改查从 controller 都可以生成,下边就是随着平时公司代码结构或者项目结构按不同场景生成的模板。

再简单说下代码执行过程:

代码的话,我就直接贴了哈,这是配置:

复制代码
#工程名,同时会根据它设置projectNameCamel属性
projectName=virtuous-auth-service
#模板  采用哪个模板生成
templatePath=my-mapper
#作者
author=kuku
#工程名缩写,用于apollo的app.properties
shortProjectName=virtuous-auth-service
#中心往应用层生成的feign名称
centerToAppFeignName=wms-ops
#输出根目录,为空则在当前目录以工程名为文件夹名输出
#比如输出在桌面 C:/Users/kuku/Desktop   不需要带工程名
outRootDir=D:\\JetBrains\\yanjiu\\code-generator
#基础包名
basePackage=com.virtuous.auth
#tables为用英文逗号隔开的表名,注意表名区分大小写
tables=tc_order
#生成的类删除表前缀
tableRemovePrefixes=tc_
#数据库配置
jdbc_username=postgres
jdbc_password=xxxx
jdbc_driver=org.postgresql.Driver
jdbc_url=jdbc:postgresql://localhost:5432/test
#模板
templates = my-mapper,my-doc,my-cloud,my-cloud-feign
sonPackage =
generatePackageName =
复制代码

main 入口类:

public class Main {
    public static void main(String[] args) throws Exception {
        Generate.Generate(FileUtil.TEMPLATE_FILE);
    }
}

文件工具类:

复制代码
package util;

import java.io.File;
import java.io.InputStream;
import java.util.List;
import java.util.Properties;

/**
 * 文件工具类
 * @author kuku
 * @description
 * @date 2022/7/31 12:20
 */

public class FileUtil {
    //默认配置文件名称
    private static final String PROPERTIES_CONFIG_NAME = "config.properties";
    //默认中心模板文件名称
    private static final String CENTER_TEMPLATE_NAME = "center-template";
    //默认应用模板文件名称
    private static final String APP_TEMPLATE_NAME = "app-template";

    //配置文件
    public static final Properties PROPERTIES = new Properties();
    //中心模板文件
    public static File CENTER_FILE = null;
    //应用模板文件
    public static File APP_FILE = null;
    //模板文件
    public static File TEMPLATE_FILE = null;

    //加载初始化
    static {
        try {
            //加载配置文件
            InputStream resourceAsStream = FileUtil.class.getClassLoader().getResourceAsStream(PROPERTIES_CONFIG_NAME);
            PROPERTIES.load(resourceAsStream);
            PrintUtil.printProperties(PROPERTIES);
            TEMPLATE_FILE = new File(PropertiesUtil.getProperty(PropertiesUtil.TEMPLATE_PATH)).getAbsoluteFile();
        }catch (Exception e) {
            e.printStackTrace();
        }
    }


    public static String getRelativePath(File templateFile, File fileItem) {
        return fileItem.getAbsolutePath().substring(templateFile.getAbsolutePath().length() + 1);
    }

    /**
     * 递归收集文件内容信息
     * @param file
     * @param list
     */
    public static void collectFiles(File file, List<File> list) {
        if (file.isHidden()) {
            return;
        }
        list.add(file);
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            for (File item : files) {
                collectFiles(item, list);
            }
        }
    }
}
复制代码

核心生成类:

复制代码
import com.google.common.collect.Lists;
import data.Table;
import freemarker.template.Configuration;
import freemarker.template.Template;
import util.FileUtil;
import util.PropertiesUtil;
import util.StringUtil;
import util.TemplateUtil;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author kuku
 * @description
 * @date 2022/7/31 13:32
 */
public class Generate {

    private static List<String> generateOneName = Lists.newArrayList(
            "pom.xml", ".gitignore", "banner.txt", "logback-spring.xml", "ddl-h2.sql", "package-info.java"
            );
    private static List<String> FileSuffixName = Lists.newArrayList(
            ".properties", "null"
    );

    public static void Generate(final File templateFile) throws Exception {
        //1、收集模板文件的文件信息
        List<File> res = Lists.newArrayList();
        FileUtil.collectFiles(templateFile, res);
        //2、获取渲染数据
        Map<String, Object> data = DataCollect.getData();
        List<Table> tables = DataCollect.getTableData();
        //3、获取模板配置
        Configuration configuration = TemplateUtil.getConfiguration(templateFile);

        //4、创建所有的目录
        List<String> dirList = res.stream().filter(File::isDirectory).map(item -> StringUtil.exchangeName(item.getAbsolutePath(), data)).collect(Collectors.toList());
        dirList.forEach(item -> new File(item).mkdirs());

        //5、按个进行模板渲染 读写
        for (File item : res) {
            if (item.isDirectory()) {
                continue;
            }
            //5.1、获取当前模板文件的相对路径
            String relativePath = FileUtil.getRelativePath(templateFile, item);
            if (item.isDirectory() || generateOneName.contains(item.getName()) || FileSuffixName.contains(getFileSuffix(item.getName()))) {
                draw(configuration, item, relativePath, data);
                continue;
            }

            tables.forEach(table -> {
                data.put("className", table.getAllFirstUpperName());
                data.put("table", table);

                try {
                    draw(configuration, item, relativePath, data);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });

        }

    }

    private static void draw(Configuration configuration, File item, String relativePath, Map<String, Object> data) throws Exception {
        Template template = configuration.getTemplate(relativePath);

        //5.2、获取生成文件的路径
        String generateFilePath = PropertiesUtil.getOutRootDirOrDefault() + "/" + StringUtil.exchangeName(relativePath, data);
        //5.3、将模板文件的内容复制到新生成的文件
        FileOutputStream fileOutputStream = new FileOutputStream(generateFilePath);
        Files.copy(Paths.get(item.getAbsolutePath()), fileOutputStream);
        //5.4、模板内容渲染
        Writer writer = new OutputStreamWriter(new FileOutputStream(generateFilePath), "UTF-8");
        template.process(data, writer);
        writer.close();
    }

    private static String getFileSuffix(String fileName) {
        if (fileName.lastIndexOf(".") != -1 && fileName.lastIndexOf(".") != 0) {
            return fileName.substring(fileName.lastIndexOf("."));
        }
        return "null";
    }

}
复制代码

3  小结

凡是有规律的,都是可以通过代码来实现的,加速效率开发,大家还有更好的方法的法欢迎指点哈。

posted @   酷酷-  阅读(124)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· Obsidian + DeepSeek:免费 AI 助力你的知识管理,让你的笔记飞起来!
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
历史上的今天:
2023-03-20 【Tomcat】【五】Connector 分析
2023-03-20 【Tomcat】【四】Pipeline-Value 管道
2023-03-20 【Tomcat】【三】Container 分析
2023-03-20 【Tomcat】【二】Tomcat 的生命周期管理
2023-03-20 【Tomcat】【一】Tomcat结构以及启动过程
2023-03-20 【Spring MVC】前置篇-Servlet 的认识
点击右上角即可分享
微信分享提示