五、自制代码生成器提高开发效率

发布时间 2023-04-22 22:29:08作者: 夏雪冬蝉

主要内容

  • 以乘车人增删改查为模板,自制单表管理,前后端生成器。
  • 学习代码生成器原理,学习freemarker。
  • 写自己的生成器,可用于导出复制excel,页面静态化等。

代码生成器的底层原理

生成器原理:使用freemarker,利用模板,生成java、vue等项目文件。
freemarker是老牌模板引擎,以前常用于页面开发,和thymeleaf类似,有需要批量生成格式固定的一类文件的需求,都可以使用freemarker来完成。
冷门知识点:excel可以另存为xml。
复杂excel导出:可以先设计好复制excel,转成xml,用xml来制作模板,再生成excel

 generator模块新增模板引擎freemarker模块

<!--模板引擎freemarker-->
 <dependency>
      <groupId>org.freemarker</groupId>
      <artifactId>freemarker</artifactId>
  </dependency> 

ftl包里用来写代码模板,例如

package com.zihans.train.generator.test;

public class ${domain} {

private String name;
}

util中存放freemarker的工具类。

 1 import freemarker.template.Configuration;
 2 import freemarker.template.DefaultObjectWrapper;
 3 import freemarker.template.Template;
 4 import freemarker.template.TemplateException;
 5 
 6 import java.io.BufferedWriter;
 7 import java.io.File;
 8 import java.io.FileWriter;
 9 import java.io.IOException;
10 import java.util.Map;
11 
12 public class FreemarkerUtil {
13 
14     static String ftlPath = "generator\\src\\main\\java\\com\\jiawa\\train\\generator\\ftl\\";
15 
16     static Template temp;
17 
18     /**
19      * 读模板
20      */
21     public static void initConfig(String ftlName) throws IOException {
22         Configuration cfg = new Configuration(Configuration.VERSION_2_3_31);
23         cfg.setDirectoryForTemplateLoading(new File(ftlPath));
24         cfg.setObjectWrapper(new DefaultObjectWrapper(Configuration.VERSION_2_3_31));
25         temp = cfg.getTemplate(ftlName);
26     }
27 
28     /**
29      * 根据模板,生成文件
30      */
31     public static void generator(String fileName, Map<String, Object> map) throws IOException, TemplateException {
32         FileWriter fw = new FileWriter(fileName);
33         BufferedWriter bw = new BufferedWriter(fw);
34         temp.process(map, bw);
35         bw.flush();
36         fw.close();
37     }
38 }
FreemarkerUtil.java

server包中新建ServerGenerator.java类,用来运行从而生成目标类。

 1 package com.zihans.train.generator.server;
 2 
 3 import com.zihans.train.generator.util.FreemarkerUtil;
 4 import freemarker.template.TemplateException;
 5 
 6 import java.io.File;
 7 import java.io.IOException;
 8 import java.util.HashMap;
 9 import java.util.Map;
10 
11 public class ServerGenerator {
12     static String toPath = "generator\\src\\main\\java\\com\\zihans\\train\\generator\\test\\";
13     static {
14         new File(toPath).mkdirs();
15     }
16 
17     public static void main(String[] args) throws IOException, TemplateException {
18         FreemarkerUtil.initConfig("test.ftl");
19         Map<String, Object> param = new HashMap<>();
20         param.put("domain", "Test1");
21         FreemarkerUtil.generator(toPath + "Test1.java", param);
22     }
23 }
ServerGenerator.java

运行后生成

1 package com.zihans.train.generator.test;
2 
3 public class Test1 {
4 
5 private String name;
6 }
Test1.java

server最好从batis generator的配置文件中读取tableName。只需要一处配置。

集成DOM4j读取xml

添加依赖

        <!-- 读xml -->
        <dependency>
            <groupId>org.dom4j</groupId>
            <artifactId>dom4j</artifactId>
            <version>2.1.3</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/jaxen/jaxen -->
        <dependency>
            <groupId>jaxen</groupId>
            <artifactId>jaxen</artifactId>
            <version>1.2.0</version>
        </dependency>
要在pom里手动开启对应模块,不用则注释掉
- <configurationFile>src/main/resources/generator-config-member.xml</configurationFile> + <!--<configurationFile>src/main/resources/generator-config-member.xml</configurationFile>--> + <configurationFile>src/main/resources/generator-config-business.xml</configurationFile>

生成生成器配置文件

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <!DOCTYPE generatorConfiguration
 3         PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
 4         "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
 5 
 6 <generatorConfiguration>
 7     <context id="Mysql" targetRuntime="MyBatis3" defaultModelType="flat">
 8 
 9         <!-- 自动检查关键字,为关键字增加反引号 -->
10         <property name="autoDelimitKeywords" value="true"/>
11         <property name="beginningDelimiter" value="`"/>
12         <property name="endingDelimiter" value="`"/>
13 
14         <!--覆盖生成XML文件-->
15         <plugin type="org.mybatis.generator.plugins.UnmergeableXmlMappersPlugin" />
16         <!-- 生成的实体类添加toString()方法 -->
17         <plugin type="org.mybatis.generator.plugins.ToStringPlugin"/>
18 
19         <!-- 不生成注释 -->
20         <commentGenerator>
21             <property name="suppressAllComments" value="true"/>
22         </commentGenerator>
23 
24         <!-- 配置数据源,需要根据自己的项目修改 -->
25         <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
26                         connectionURL="jdbc:mysql://rm-uf600hmft8m2272o2uo.rwlb.rds.aliyuncs.com/train_business?serverTimezone=Asia/Shanghai"
27                         userId="train_business"
28                         password="Business123">
29         </jdbcConnection>
30 
31         <!-- domain类的位置 targetProject是相对pom.xml的路径-->
32         <javaModelGenerator targetProject="..\member\src\main\java"
33                             targetPackage="com.jiawa.train.member.domain"/>
34 
35         <!-- mapper xml的位置 targetProject是相对pom.xml的路径 -->
36         <sqlMapGenerator targetProject="..\member\src\main\resources"
37                          targetPackage="mapper"/>
38 
39         <!-- mapper类的位置 targetProject是相对pom.xml的路径 -->
40         <javaClientGenerator targetProject="..\member\src\main\java"
41                              targetPackage="com.jiawa.train.member.mapper"
42                              type="XMLMAPPER"/>
43 
44         <!--<table tableName="member" domainObjectName="Member"/>-->
45         <table tableName="passenger" domainObjectName="Passenger"/>
46     </context>
47 </generatorConfiguration>
generator-config-business.xml

读取并输出上面的xml文件目录(用于测试DOM4j)

 1 package com.zihans.train.generator.server;
 2 
 3 import org.dom4j.Document;
 4 import org.dom4j.Node;
 5 import org.dom4j.io.SAXReader;
 6 
 7 import java.io.File;
 8 import java.util.HashMap;
 9 import java.util.Map;
10 
11 public class ServerGenerator {
12     static String toPath = "generator\\src\\main\\java\\com\\zihans\\train\\generator\\test\\";
13     static String pomPath = "generator\\pom.xml";
14     static {
15         new File(toPath).mkdirs();
16     }
17 
18     public static void main(String[] args) throws Exception {
19         SAXReader saxReader = new SAXReader();
20         Map<String, String> map = new HashMap<String, String>();
21         map.put("pom", "http://maven.apache.org/POM/4.0.0");
22         saxReader.getDocumentFactory().setXPathNamespaceURIs(map);
23         Document document = saxReader.read(pomPath);
24         Node node = document.selectSingleNode("//pom:configurationFile");
25         System.out.println(node.getText());
26 
27         // FreemarkerUtil.initConfig("test.ftl");
28         // Map<String, Object> param = new HashMap<>();
29         // param.put("domain", "Test1");
30         // FreemarkerUtil.generator(toPath + "Test1.java", param);
31     }
32 }
ServerGenerator.java

集成dom4j,读取当前持久层的xml文件,得到表名和实体名

 1 package com.zihans.train.generator.server;
 2 
 3 import org.dom4j.Document;
 4 import org.dom4j.DocumentException;
 5 import org.dom4j.Node;
 6 import org.dom4j.io.SAXReader;
 7 
 8 import java.io.File;
 9 import java.util.HashMap;
10 import java.util.Map;
11 
12 public class ServerGenerator {
13     static String toPath = "generator\\src\\main\\java\\com\\zihans\\train\\generator\\test\\";
14     static String pomPath = "generator\\pom.xml";
15     static {
16         new File(toPath).mkdirs();
17     }
18 
19     public static void main(String[] args) throws Exception {
20         String generatorPath = getGeneratorPath();
21 
22         Document document = new SAXReader().read("generator/" + generatorPath);
23         Node table = document.selectSingleNode("//table");
24         System.out.println(table);
25         Node tableName = table.selectSingleNode("@tableName");
26         Node domainObjectName = table.selectSingleNode("@domainObjectName");
27         System.out.println(tableName.getText() + "/" + domainObjectName.getText());
28 
29         // FreemarkerUtil.initConfig("test.ftl");
30         // Map<String, Object> param = new HashMap<>();
31         // param.put("domain", "Test1");
32         // FreemarkerUtil.generator(toPath + "Test1.java", param);
33     }
34 
35     private static String getGeneratorPath() throws DocumentException {
36         SAXReader saxReader = new SAXReader();
37         Map<String, String> map = new HashMap<String, String>();
38         map.put("pom", "http://maven.apache.org/POM/4.0.0");
39         saxReader.getDocumentFactory().setXPathNamespaceURIs(map);
40         Document document = saxReader.read(pomPath);
41         Node node = document.selectSingleNode("//pom:configurationFile");
42         System.out.println(node.getText());
43         return node.getText();
44     }
45 }
ServerGenerator.java

使用PassengerService制作模板,生成service成功

模板代码

 1 package com.zihans.train.member.service;
 2 
 3 import cn.hutool.core.bean.BeanUtil;
 4 import cn.hutool.core.date.DateTime;
 5 import cn.hutool.core.util.ObjectUtil;
 6 import com.github.pagehelper.PageHelper;
 7 import com.github.pagehelper.PageInfo;
 8 import com.zihans.train.common.context.LoginMemberContext;
 9 import com.zihans.train.common.resp.PageResp;
10 import com.zihans.train.common.util.SnowUtil;
11 import com.zihans.train.member.domain.${Domain};
12 import com.zihans.train.member.domain.${Domain}Example;
13 import com.zihans.train.member.mapper.${Domain}Mapper;
14 import com.zihans.train.member.req.${Domain}QueryReq;
15 import com.zihans.train.member.req.${Domain}SaveReq;
16 import com.zihans.train.member.resp.${Domain}QueryResp;
17 import jakarta.annotation.Resource;
18 import org.slf4j.Logger;
19 import org.slf4j.LoggerFactory;
20 import org.springframework.stereotype.Service;
21 
22 import java.util.List;
23 
24 @Service
25 public class ${Domain}Service {
26 
27     private static final Logger LOG = LoggerFactory.getLogger(${Domain}Service.class);
28 
29     @Resource
30     private ${Domain}Mapper ${domain}Mapper;
31 
32     public void save(${Domain}SaveReq req) {
33         DateTime now = DateTime.now();
34         ${Domain} ${domain} = BeanUtil.copyProperties(req, ${Domain}.class);
35         if (ObjectUtil.isNull(${domain}.getId())) {
36             ${domain}.setMemberId(LoginMemberContext.getId());
37             ${domain}.setId(SnowUtil.getSnowflakeNextId());
38             ${domain}.setCreateTime(now);
39             ${domain}.setUpdateTime(now);
40             ${domain}Mapper.insert(${domain});
41         } else {
42             ${domain}.setUpdateTime(now);
43             ${domain}Mapper.updateByPrimaryKey(${domain});
44         }
45     }
46 
47     public PageResp<${Domain}QueryResp> queryList(${Domain}QueryReq req) {
48         ${Domain}Example ${domain}Example = new ${Domain}Example();
49         ${domain}Example.setOrderByClause("id desc");
50         ${Domain}Example.Criteria criteria = ${domain}Example.createCriteria();
51         if (ObjectUtil.isNotNull(req.getMemberId())) {
52             criteria.andMemberIdEqualTo(req.getMemberId());
53         }
54 
55         LOG.info("查询页码:{}", req.getPage());
56         LOG.info("每页条数:{}", req.getSize());
57         PageHelper.startPage(req.getPage(), req.getSize());
58         List<${Domain}> ${domain}List = ${domain}Mapper.selectByExample(${domain}Example);
59 
60         PageInfo<${Domain}> pageInfo = new PageInfo<>(${domain}List);
61         LOG.info("总行数:{}", pageInfo.getTotal());
62         LOG.info("总页数:{}", pageInfo.getPages());
63 
64         List<${Domain}QueryResp> list = BeanUtil.copyToList(${domain}List, ${Domain}QueryResp.class);
65 
66         PageResp<${Domain}QueryResp> pageResp = new PageResp<>();
67         pageResp.setTotal(pageInfo.getTotal());
68         pageResp.setList(list);
69         return pageResp;
70     }
71 
72     public void delete(Long id) {
73         ${domain}Mapper.deleteByPrimaryKey(id);
74     }
75 }
service.ftl

生成器

 1 package com.zihans.train.generator.server;
 2 
 3 
 4 import com.zihans.train.generator.util.FreemarkerUtil;
 5 import org.dom4j.Document;
 6 import org.dom4j.DocumentException;
 7 import org.dom4j.Node;
 8 import org.dom4j.io.SAXReader;
 9 
10 import java.io.File;
11 import java.util.HashMap;
12 import java.util.Map;
13 
14 public class ServerGenerator {
15     static String servicePath = "[module]/src/main/java/com/zihans/train/[module]/service/";
16     static String pomPath = "generator\\pom.xml";
17     static {
18         new File(servicePath).mkdirs();
19     }
20 
21     public static void main(String[] args) throws Exception {
22         // 获取mybatis-generator
23         String generatorPath = getGeneratorPath();
24         // 比如generator-config-member.xml,得到module = member
25         String module = generatorPath.replace("src/main/resources/generator-config-", "").replace(".xml", "");
26         System.out.println("module: " + module);
27         servicePath = servicePath.replace("[module]", module);
28         // new File(servicePath).mkdirs();
29         System.out.println("servicePath: " + servicePath);
30 
31         // 读取table节点
32         Document document = new SAXReader().read("generator/" + generatorPath);
33         Node table = document.selectSingleNode("//table");
34         System.out.println(table);
35         Node tableName = table.selectSingleNode("@tableName");
36         Node domainObjectName = table.selectSingleNode("@domainObjectName");
37         System.out.println(tableName.getText() + "/" + domainObjectName.getText());
38 
39         // 示例:表名 jiawa_test
40         // Domain = JiawaTest
41         String Domain = domainObjectName.getText();
42         // domain = jiawaTest
43         String domain = Domain.substring(0, 1).toLowerCase() + Domain.substring(1);
44         // do_main = jiawa-test
45         String do_main = tableName.getText().replaceAll("_", "-");
46 
47         // 组装参数
48         Map<String, Object> param = new HashMap<>();
49         param.put("Domain", Domain);
50         param.put("domain", domain);
51         param.put("do_main", do_main);
52         System.out.println("组装参数:" + param);
53 
54         FreemarkerUtil.initConfig("service.ftl");
55         FreemarkerUtil.generator(servicePath + Domain + "Service.java", param);
56     }
57 
58     private static String getGeneratorPath() throws DocumentException {
59         SAXReader saxReader = new SAXReader();
60         Map<String, String> map = new HashMap<String, String>();
61         map.put("pom", "http://maven.apache.org/POM/4.0.0");
62         saxReader.getDocumentFactory().setXPathNamespaceURIs(map);
63         Document document = saxReader.read(pomPath);
64         Node node = document.selectSingleNode("//pom:configurationFile");
65         System.out.println(node.getText());
66         return node.getText();
67     }
68 }
ServerGenerator.java

使用PassengerController制作模板,生成controller成功

 1 package com.zihans.train.member.controller;
 2 
 3 import com.zihans.train.common.context.LoginMemberContext;
 4 import com.zihans.train.common.resp.CommonResp;
 5 import com.zihans.train.common.resp.PageResp;
 6 import com.zihans.train.member.req.${Domain}QueryReq;
 7 import com.zihans.train.member.req.${Domain}SaveReq;
 8 import com.zihans.train.member.resp.${Domain}QueryResp;
 9 import com.zihans.train.member.service.${Domain}Service;
10 import jakarta.annotation.Resource;
11 import jakarta.validation.Valid;
12 import org.springframework.web.bind.annotation.*;
13 
14 @RestController
15 @RequestMapping("/${do_main}")
16 public class ${Domain}Controller {
17 
18     @Resource
19     private ${Domain}Service ${domain}Service;
20 
21     @PostMapping("/save")
22     public CommonResp<Object> save(@Valid @RequestBody ${Domain}SaveReq req) {
23         ${domain}Service.save(req);
24         return new CommonResp<>();
25     }
26 
27     @GetMapping("/query-list")
28     public CommonResp<PageResp<${Domain}QueryResp>> queryList(@Valid ${Domain}QueryReq req) {
29         req.setMemberId(LoginMemberContext.getId());
30         PageResp<${Domain}QueryResp> list = ${domain}Service.queryList(req);
31         return new CommonResp<>(list);
32     }
33 
34     @DeleteMapping("/delete/{id}")
35     public CommonResp<Object> delete(@PathVariable Long id) {
36         ${domain}Service.delete(id);
37         return new CommonResp<>();
38     }
39 
40 }
controller.ftl
 1 package com.zihans.train.generator.server;
 2 
 3 import com.zihans.train.generator.util.FreemarkerUtil;
 4 import freemarker.template.TemplateException;
 5 import org.dom4j.Document;
 6 import org.dom4j.DocumentException;
 7 import org.dom4j.Node;
 8 import org.dom4j.io.SAXReader;
 9 
10 import java.io.File;
11 import java.io.IOException;
12 import java.util.HashMap;
13 import java.util.Map;
14 
15 public class ServerGenerator {
16     static String serverPath = "[module]/src/main/java/com/zihans/train/[module]/";
17     static String pomPath = "generator\\pom.xml";
18     static {
19         new File(serverPath).mkdirs();
20     }
21 
22     public static void main(String[] args) throws Exception {
23         // 获取mybatis-generator
24         String generatorPath = getGeneratorPath();
25         // 比如generator-config-member.xml,得到module = member
26         String module = generatorPath.replace("src/main/resources/generator-config-", "").replace(".xml", "");
27         System.out.println("module: " + module);
28         serverPath = serverPath.replace("[module]", module);
29         // new File(servicePath).mkdirs();
30         System.out.println("servicePath: " + serverPath);
31 
32         // 读取table节点
33         Document document = new SAXReader().read("generator/" + generatorPath);
34         Node table = document.selectSingleNode("//table");
35         System.out.println(table);
36         Node tableName = table.selectSingleNode("@tableName");
37         Node domainObjectName = table.selectSingleNode("@domainObjectName");
38         System.out.println(tableName.getText() + "/" + domainObjectName.getText());
39 
40         // 示例:表名 jiawa_test
41         // Domain = JiawaTest
42         String Domain = domainObjectName.getText();
43         // domain = jiawaTest
44         String domain = Domain.substring(0, 1).toLowerCase() + Domain.substring(1);
45         // do_main = jiawa-test
46         String do_main = tableName.getText().replaceAll("_", "-");
47 
48         // 组装参数
49         Map<String, Object> param = new HashMap<>();
50         param.put("Domain", Domain);
51         param.put("domain", domain);
52         param.put("do_main", do_main);
53         System.out.println("组装参数:" + param);
54 
55         gen(Domain, param, "service");
56         gen(Domain, param, "controller");
57     }
58 
59     private static void gen(String Domain, Map<String, Object> param, String target) throws IOException, TemplateException {
60         FreemarkerUtil.initConfig(target + ".ftl");
61         String toPath = serverPath + target + "/";
62         new File(toPath).mkdirs();
63         String Target = target.substring(0, 1).toUpperCase() + target.substring(1);
64         String fileName = toPath + Domain + Target + ".java";
65         System.out.println("开始生成:" + fileName);
66         FreemarkerUtil.generator(fileName, param);
67     }
68 
69     private static String getGeneratorPath() throws DocumentException {
70         SAXReader saxReader = new SAXReader();
71         Map<String, String> map = new HashMap<String, String>();
72         map.put("pom", "http://maven.apache.org/POM/4.0.0");
73         saxReader.getDocumentFactory().setXPathNamespaceURIs(map);
74         Document document = saxReader.read(pomPath);
75         Node node = document.selectSingleNode("//pom:configurationFile");
76         System.out.println(node.getText());
77         return node.getText();
78     }
79 }
ServerGenerator.java

制作DBUtil读取表字段信息

制作实体类需要根据表名获取里面的具体字段。

增加Field.java类,用来描述表里面每一个的字段的信息的实体类。

  1 package com.zihans.train.generator.util;
  2 
  3 public class Field {
  4     private String name; // 字段名:course_id
  5     private String nameHump; // 字段名小驼峰:courseId
  6     private String nameBigHump; // 字段名大驼峰:CourseId
  7     private String nameCn; // 中文名:课程
  8     private String type; // 字段类型:char(8)
  9     private String javaType; // java类型:String
 10     private String comment; // 注释:课程|ID
 11     private Boolean nullAble; // 是否可为空
 12     private Integer length; // 字符串长度
 13     private Boolean enums; // 是否是枚举
 14     private String enumsConst; // 枚举常量 COURSE_LEVEL
 15 
 16     public String getName() {
 17         return name;
 18     }
 19 
 20     public void setName(String name) {
 21         this.name = name;
 22     }
 23 
 24     public String getNameHump() {
 25         return nameHump;
 26     }
 27 
 28     public void setNameHump(String nameHump) {
 29         this.nameHump = nameHump;
 30     }
 31 
 32     public String getNameBigHump() {
 33         return nameBigHump;
 34     }
 35 
 36     public void setNameBigHump(String nameBigHump) {
 37         this.nameBigHump = nameBigHump;
 38     }
 39 
 40     public String getNameCn() {
 41         return nameCn;
 42     }
 43 
 44     public void setNameCn(String nameCn) {
 45         this.nameCn = nameCn;
 46     }
 47 
 48     public String getType() {
 49         return type;
 50     }
 51 
 52     public void setType(String type) {
 53         this.type = type;
 54     }
 55 
 56     public String getComment() {
 57         return comment;
 58     }
 59 
 60     public void setComment(String comment) {
 61         this.comment = comment;
 62     }
 63 
 64     public String getJavaType() {
 65         return javaType;
 66     }
 67 
 68     public void setJavaType(String javaType) {
 69         this.javaType = javaType;
 70     }
 71 
 72     public Boolean getNullAble() {
 73         return nullAble;
 74     }
 75 
 76     public void setNullAble(Boolean nullAble) {
 77         this.nullAble = nullAble;
 78     }
 79 
 80     public Integer getLength() {
 81         return length;
 82     }
 83 
 84     public void setLength(Integer length) {
 85         this.length = length;
 86     }
 87 
 88     public Boolean getEnums() {
 89         return enums;
 90     }
 91 
 92     public void setEnums(Boolean enums) {
 93         this.enums = enums;
 94     }
 95 
 96     public String getEnumsConst() {
 97         return enumsConst;
 98     }
 99 
100     public void setEnumsConst(String enumsConst) {
101         this.enumsConst = enumsConst;
102     }
103 
104     @Override
105     public String toString() {
106         final StringBuffer sb = new StringBuffer("Field{");
107         sb.append("name='").append(name).append('\'');
108         sb.append(", nameHump='").append(nameHump).append('\'');
109         sb.append(", nameBigHump='").append(nameBigHump).append('\'');
110         sb.append(", nameCn='").append(nameCn).append('\'');
111         sb.append(", type='").append(type).append('\'');
112         sb.append(", javaType='").append(javaType).append('\'');
113         sb.append(", comment='").append(comment).append('\'');
114         sb.append(", nullAble=").append(nullAble);
115         sb.append(", length=").append(length);
116         sb.append(", enums=").append(enums);
117         sb.append(", enumsConst='").append(enumsConst).append('\'');
118         sb.append('}');
119         return sb.toString();
120     }
121 }
Field.java

再写一个DBUtil.java

  1 package com.zihans.train.generator.util;
  2 
  3 import cn.hutool.core.util.StrUtil;
  4 
  5 import java.sql.*;
  6 import java.util.ArrayList;
  7 import java.util.List;
  8 import java.util.regex.Matcher;
  9 import java.util.regex.Pattern;
 10 
 11 public class DbUtil {
 12 
 13     public static String url = "";
 14     public static String user = "";
 15     public static String password = "";
 16 
 17     public static Connection getConnection() {
 18         Connection conn = null;
 19         try {
 20             Class.forName("com.mysql.cj.jdbc.Driver");
 21             String url = DbUtil.url;
 22             String user = DbUtil.user;
 23             String password = DbUtil.password;
 24             conn = DriverManager.getConnection(url, user, password);
 25         } catch (ClassNotFoundException e) {
 26             e.printStackTrace();
 27         } catch (SQLException e) {
 28             e.printStackTrace();
 29         }
 30         return conn;
 31     }
 32 
 33     /**
 34      * 获得表注释
 35      * @param tableName
 36      * @return
 37      * @throws Exception
 38      */
 39     public static String getTableComment(String tableName) throws Exception {
 40         Connection conn = getConnection();
 41         Statement stmt = conn.createStatement();
 42         ResultSet rs = stmt.executeQuery("select table_comment from information_schema.tables Where table_name = '" + tableName + "'");
 43         String tableNameCH = "";
 44         if (rs != null) {
 45             while(rs.next()) {
 46                 tableNameCH = rs.getString("table_comment");
 47                 break;
 48             }
 49         }
 50         rs.close();
 51         stmt.close();
 52         conn.close();
 53         System.out.println("表名:" + tableNameCH);
 54         return tableNameCH;
 55     }
 56 
 57     /**
 58      * 获得所有列信息
 59      * @param tableName
 60      * @return
 61      * @throws Exception
 62      */
 63     public static List<Field> getColumnByTableName(String tableName) throws Exception {
 64         List<Field> fieldList = new ArrayList<>();
 65         Connection conn = getConnection();
 66         Statement stmt = conn.createStatement();
 67         ResultSet rs = stmt.executeQuery("show full columns from `" + tableName + "`");
 68         if (rs != null) {
 69             while(rs.next()) {
 70                 String columnName = rs.getString("Field");
 71                 String type = rs.getString("Type");
 72                 String comment = rs.getString("Comment");
 73                 String nullAble = rs.getString("Null"); //YES NO
 74                 Field field = new Field();
 75                 field.setName(columnName);
 76                 field.setNameHump(lineToHump(columnName));
 77                 field.setNameBigHump(lineToBigHump(columnName));
 78                 field.setType(type);
 79                 field.setJavaType(DbUtil.sqlTypeToJavaType(rs.getString("Type")));
 80                 field.setComment(comment);
 81                 if (comment.contains("|")) {
 82                     field.setNameCn(comment.substring(0, comment.indexOf("|")));
 83                 } else {
 84                     field.setNameCn(comment);
 85                 }
 86                 field.setNullAble("YES".equals(nullAble));
 87                 if (type.toUpperCase().contains("varchar".toUpperCase())) {
 88                     String lengthStr = type.substring(type.indexOf("(") + 1, type.length() - 1);
 89                     field.setLength(Integer.valueOf(lengthStr));
 90                 } else {
 91                     field.setLength(0);
 92                 }
 93                 if (comment.contains("枚举")) {
 94                     field.setEnums(true);
 95 
 96                     // 以课程等级为例:从注释中的“枚举[CourseLevelEnum]”,得到enumsConst = COURSE_LEVEL
 97                     int start = comment.indexOf("[");
 98                     int end = comment.indexOf("]");
 99                     String enumsName = comment.substring(start + 1, end); // CourseLevelEnum
100                     String enumsConst = StrUtil.toUnderlineCase(enumsName)
101                             .toUpperCase().replace("_ENUM", "");
102                     field.setEnumsConst(enumsConst);
103                 } else {
104                     field.setEnums(false);
105                 }
106                 fieldList.add(field);
107             }
108         }
109         rs.close();
110         stmt.close();
111         conn.close();
112         System.out.println("列信息:" + fieldList);
113         return fieldList;
114     }
115 
116     /**
117      * 下划线转小驼峰:member_id 转成 memberId
118      */
119     public static String lineToHump(String str){
120         Pattern linePattern = Pattern.compile("_(\\w)");
121         str = str.toLowerCase();
122         Matcher matcher = linePattern.matcher(str);
123         StringBuffer sb = new StringBuffer();
124         while(matcher.find()){
125             matcher.appendReplacement(sb, matcher.group(1).toUpperCase());
126         }
127         matcher.appendTail(sb);
128         return sb.toString();
129     }
130 
131     /**
132      * 下划线转大驼峰:member_id 转成 MemberId
133      */
134     public static String lineToBigHump(String str){
135         String s = lineToHump(str);
136         return s.substring(0, 1).toUpperCase() + s.substring(1);
137     }
138 
139     /**
140      * 数据库类型转为Java类型
141      */
142     public static String sqlTypeToJavaType(String sqlType) {
143         if (sqlType.toUpperCase().contains("varchar".toUpperCase())
144                 || sqlType.toUpperCase().contains("char".toUpperCase())
145                 || sqlType.toUpperCase().contains("text".toUpperCase())) {
146             return "String";
147         } else if (sqlType.toUpperCase().contains("datetime".toUpperCase())) {
148             return "Date";
149         } else if (sqlType.toUpperCase().contains("bigint".toUpperCase())) {
150             return "Long";
151         } else if (sqlType.toUpperCase().contains("int".toUpperCase())) {
152             return "Integer";
153         } else if (sqlType.toUpperCase().contains("long".toUpperCase())) {
154             return "Long";
155         } else if (sqlType.toUpperCase().contains("decimal".toUpperCase())) {
156             return "BigDecimal";
157         } else if (sqlType.toUpperCase().contains("boolean".toUpperCase())) {
158             return "Boolean";
159         } else {
160             return "String";
161         }
162     }
163 }
DbUtil.java
 1 package com.zihans.train.generator.server;
 2 
 3 import com.zihans.train.generator.util.DbUtil;
 4 import com.zihans.train.generator.util.Field;
 5 import com.zihans.train.generator.util.FreemarkerUtil;
 6 import freemarker.template.TemplateException;
 7 import org.dom4j.Document;
 8 import org.dom4j.DocumentException;
 9 import org.dom4j.Node;
10 import org.dom4j.io.SAXReader;
11 
12 import java.io.File;
13 import java.io.IOException;
14 import java.util.HashMap;
15 import java.util.List;
16 import java.util.Map;
17 
18 public class ServerGenerator {
19     static String serverPath = "[module]/src/main/java/com/zihans/train/[module]/";
20     static String pomPath = "generator\\pom.xml";
21     static {
22         new File(serverPath).mkdirs();
23     }
24 
25     public static void main(String[] args) throws Exception {
26         // 获取mybatis-generator
27         String generatorPath = getGeneratorPath();
28         // 比如generator-config-member.xml,得到module = member
29         String module = generatorPath.replace("src/main/resources/generator-config-", "").replace(".xml", "");
30         System.out.println("module: " + module);
31         serverPath = serverPath.replace("[module]", module);
32         // new File(servicePath).mkdirs();
33         System.out.println("servicePath: " + serverPath);
34 
35         // 读取table节点
36         Document document = new SAXReader().read("generator/" + generatorPath);
37         Node table = document.selectSingleNode("//table");
38         System.out.println(table);
39         Node tableName = table.selectSingleNode("@tableName");
40         Node domainObjectName = table.selectSingleNode("@domainObjectName");
41         System.out.println(tableName.getText() + "/" + domainObjectName.getText());
42 
43         // 为DbUtil设置数据源
44         Node connectionURL = document.selectSingleNode("//@connectionURL");
45         Node userId = document.selectSingleNode("//@userId");
46         Node password = document.selectSingleNode("//@password");
47         System.out.println("url: " + connectionURL.getText());
48         System.out.println("user: " + userId.getText());
49         System.out.println("password: " + password.getText());
50         DbUtil.url = connectionURL.getText();
51         DbUtil.user = userId.getText();
52         DbUtil.password = password.getText();
53 
54         // 示例:表名 jiawa_test
55         // Domain = JiawaTest
56         String Domain = domainObjectName.getText();
57         // domain = jiawaTest
58         String domain = Domain.substring(0, 1).toLowerCase() + Domain.substring(1);
59         // do_main = jiawa-test
60         String do_main = tableName.getText().replaceAll("_", "-");
61         // 表中文名
62         String tableNameCn = DbUtil.getTableComment(tableName.getText());
63         List<Field> fieldList = DbUtil.getColumnByTableName(tableName.getText());
64 
65         // 组装参数
66         Map<String, Object> param = new HashMap<>();
67         param.put("Domain", Domain);
68         param.put("domain", domain);
69         param.put("do_main", do_main);
70         System.out.println("组装参数:" + param);
71 
72         gen(Domain, param, "service");
73         gen(Domain, param, "controller");
74     }
75 
76     private static void gen(String Domain, Map<String, Object> param, String target) throws IOException, TemplateException {
77         FreemarkerUtil.initConfig(target + ".ftl");
78         String toPath = serverPath + target + "/";
79         new File(toPath).mkdirs();
80         String Target = target.substring(0, 1).toUpperCase() + target.substring(1);
81         String fileName = toPath + Domain + Target + ".java";
82         System.out.println("开始生成:" + fileName);
83         FreemarkerUtil.generator(fileName, param);
84     }
85 
86     private static String getGeneratorPath() throws DocumentException {
87         SAXReader saxReader = new SAXReader();
88         Map<String, String> map = new HashMap<String, String>();
89         map.put("pom", "http://maven.apache.org/POM/4.0.0");
90         saxReader.getDocumentFactory().setXPathNamespaceURIs(map);
91         Document document = saxReader.read(pomPath);
92         Node node = document.selectSingleNode("//pom:configurationFile");
93         System.out.println(node.getText());
94         return node.getText();
95     }
96 }
ServerGenerator.java

详解实体类生成器

  1 package com.zihans.train.generator.server;
  2 
  3 import com.zihans.train.generator.util.DbUtil;
  4 import com.zihans.train.generator.util.Field;
  5 import com.zihans.train.generator.util.FreemarkerUtil;
  6 import freemarker.template.TemplateException;
  7 import org.dom4j.Document;
  8 import org.dom4j.DocumentException;
  9 import org.dom4j.Node;
 10 import org.dom4j.io.SAXReader;
 11 
 12 import java.io.File;
 13 import java.io.IOException;
 14 import java.util.*;
 15 
 16 public class ServerGenerator {
 17     static String serverPath = "[module]/src/main/java/com/zihans/train/[module]/";
 18     static String pomPath = "generator\\pom.xml";
 19     static {
 20         new File(serverPath).mkdirs();
 21     }
 22 
 23     public static void main(String[] args) throws Exception {
 24         // 获取mybatis-generator
 25         String generatorPath = getGeneratorPath();
 26         // 比如generator-config-member.xml,得到module = member
 27         String module = generatorPath.replace("src/main/resources/generator-config-", "").replace(".xml", "");
 28         System.out.println("module: " + module);
 29         serverPath = serverPath.replace("[module]", module);
 30         // new File(servicePath).mkdirs();
 31         System.out.println("servicePath: " + serverPath);
 32 
 33         // 读取table节点
 34         Document document = new SAXReader().read("generator/" + generatorPath);
 35         Node table = document.selectSingleNode("//table");
 36         System.out.println(table);
 37         Node tableName = table.selectSingleNode("@tableName");
 38         Node domainObjectName = table.selectSingleNode("@domainObjectName");
 39         System.out.println(tableName.getText() + "/" + domainObjectName.getText());
 40 
 41         // 为DbUtil设置数据源
 42         Node connectionURL = document.selectSingleNode("//@connectionURL");
 43         Node userId = document.selectSingleNode("//@userId");
 44         Node password = document.selectSingleNode("//@password");
 45         System.out.println("url: " + connectionURL.getText());
 46         System.out.println("user: " + userId.getText());
 47         System.out.println("password: " + password.getText());
 48         DbUtil.url = connectionURL.getText();
 49         DbUtil.user = userId.getText();
 50         DbUtil.password = password.getText();
 51 
 52         // 示例:表名 jiawa_test
 53         // Domain = JiawaTest
 54         String Domain = domainObjectName.getText();
 55         // domain = jiawaTest
 56         String domain = Domain.substring(0, 1).toLowerCase() + Domain.substring(1);
 57         // do_main = jiawa-test
 58         String do_main = tableName.getText().replaceAll("_", "-");
 59         // 表中文名
 60         String tableNameCn = DbUtil.getTableComment(tableName.getText());
 61         List<Field> fieldList = DbUtil.getColumnByTableName(tableName.getText());
 62         Set<String> typeSet = getJavaTypes(fieldList);
 63 
 64         // 组装参数
 65         Map<String, Object> param = new HashMap<>();
 66         param.put("Domain", Domain);
 67         param.put("domain", domain);
 68         param.put("do_main", do_main);
 69         param.put("tableNameCn", tableNameCn);
 70         param.put("fieldList", fieldList);
 71         param.put("typeSet", typeSet);
 72         System.out.println("组装参数:" + param);
 73 
 74         gen(Domain, param, "service", "service");
 75         gen(Domain, param, "controller", "controller");
 76         gen(Domain, param, "req", "saveReq");
 77     }
 78 
 79     private static void gen(String Domain, Map<String, Object> param, String packageName, String target) throws IOException, TemplateException {
 80         FreemarkerUtil.initConfig(target + ".ftl");
 81         String toPath = serverPath + packageName + "/";
 82         new File(toPath).mkdirs();
 83         String Target = target.substring(0, 1).toUpperCase() + target.substring(1);
 84         String fileName = toPath + Domain + Target + ".java";
 85         System.out.println("开始生成:" + fileName);
 86         FreemarkerUtil.generator(fileName, param);
 87     }
 88 
 89     private static String getGeneratorPath() throws DocumentException {
 90         SAXReader saxReader = new SAXReader();
 91         Map<String, String> map = new HashMap<String, String>();
 92         map.put("pom", "http://maven.apache.org/POM/4.0.0");
 93         saxReader.getDocumentFactory().setXPathNamespaceURIs(map);
 94         Document document = saxReader.read(pomPath);
 95         Node node = document.selectSingleNode("//pom:configurationFile");
 96         System.out.println(node.getText());
 97         return node.getText();
 98     }
 99 
100     /**
101      * 获取所有的Java类型,使用Set去重
102      */
103     private static Set<String> getJavaTypes(List<Field> fieldList) {
104         Set<String> set = new HashSet<>();
105         for (int i = 0; i < fieldList.size(); i++) {
106             Field field = fieldList.get(i);
107             set.add(field.getJavaType());
108         }
109         return set;
110     }
111 }
ServerGenerator.java
 1 package com.zihans.train.member.req;
 2 
 3 <#list typeSet as type>
 4         <#if type=='Date'>
 5 import java.util.Date;
 6 import com.fasterxml.jackson.annotation.JsonFormat;
 7 </#if>
 8         <#if type=='BigDecimal'>
 9 import java.math.BigDecimal;
10 </#if>
11         </#list>
12 
13 import jakarta.validation.constraints.NotBlank;
14 import jakarta.validation.constraints.NotNull;
15 
16 public class ${Domain}SaveReq {
17 
18     <#list fieldList as field>
19     /**
20      * ${field.comment}
21      */
22     <#if field.javaType=='Date'>
23     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
24     </#if>
25     <#if field.name!="id" && field.nameHump!="createdAt" && field.nameHump!="updatedAt">
26         <#if !field.nullAble>
27             <#if field.javaType=='String'>
28     @NotBlank(message = "【${field.nameCn}】不能为空")
29             <#else>
30     @NotNull(message = "【${field.nameCn}】不能为空")
31             </#if>
32         </#if>
33     </#if>
34     private ${field.javaType} ${field.nameHump};
35 
36     </#list>
37     <#list fieldList as field>
38     public ${field.javaType} get${field.nameBigHump}() {
39         return ${field.nameHump};
40     }
41 
42     public void set${field.nameBigHump}(${field.javaType} ${field.nameHump}) {
43         this.${field.nameHump} = ${field.nameHump};
44     }
45 
46     </#list>
47     @Override
48     public String toString() {
49         StringBuilder sb = new StringBuilder();
50         sb.append(getClass().getSimpleName());
51         sb.append(" [");
52         sb.append("Hash = ").append(hashCode());
53         <#list fieldList as field>
54         sb.append(", ${field.nameHump}=").append(${field.nameHump});
55         </#list>
56                 sb.append("]");
57         return sb.toString();
58     }
59 }
saveReq.ftl

按模板生成代码

ServerGenerator.java新增参数

param.put("module", module);

其余ftl模块引用地址的member改为${module}

制作queryReq.ftl模板

 1 package com.zihans.train.${module}.req;
 2 
 3 import com.zihans.train.common.req.PageReq;
 4 
 5 public class ${Domain}QueryReq extends PageReq {
 6 
 7     @Override
 8     public String toString() {
 9         return "${Domain}QueryReq{" +
10                 "} " + super.toString();
11     }
12 }
queryReq.ftl

制作queryResp.ftl模板

 1 package com.zihans.train.${module}.resp;
 2 
 3 import com.fasterxml.jackson.databind.annotation.JsonSerialize;
 4 import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
 5 <#list typeSet as type>
 6 <#if type=='Date'>
 7 import java.util.Date;
 8 import com.fasterxml.jackson.annotation.JsonFormat;
 9 </#if>
10 <#if type=='BigDecimal'>
11 import java.math.BigDecimal;
12 </#if>
13 </#list>
14 
15 public class ${Domain}QueryResp {
16 
17     <#list fieldList as field>
18     /**
19      * ${field.comment}
20      */
21     <#if field.javaType=='Date'>
22         <#if field.type=='time'>
23     @JsonFormat(pattern = "HH:mm:ss",timezone = "GMT+8")
24         <#elseif field.type=='date'>
25     @JsonFormat(pattern = "yyyy-MM-dd",timezone = "GMT+8")
26         <#else>
27     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
28         </#if>
29     </#if>
30     <#if field.name=='id' || field.name?ends_with('_id')>
31     @JsonSerialize(using= ToStringSerializer.class)
32     </#if>
33     private ${field.javaType} ${field.nameHump};
34 
35     </#list>
36     <#list fieldList as field>
37     public ${field.javaType} get${field.nameBigHump}() {
38         return ${field.nameHump};
39     }
40 
41     public void set${field.nameBigHump}(${field.javaType} ${field.nameHump}) {
42         this.${field.nameHump} = ${field.nameHump};
43     }
44 
45     </#list>
46     @Override
47     public String toString() {
48         StringBuilder sb = new StringBuilder();
49         sb.append(getClass().getSimpleName());
50         sb.append(" [");
51         sb.append("Hash = ").append(hashCode());
52         <#list fieldList as field>
53         sb.append(", ${field.nameHump}=").append(${field.nameHump});
54         </#list>
55         sb.append("]");
56         return sb.toString();
57     }
58 }
queryResp.ftl
  1 package com.zihans.train.generator.server;
  2 
  3 import com.zihans.train.generator.util.DbUtil;
  4 import com.zihans.train.generator.util.Field;
  5 import com.zihans.train.generator.util.FreemarkerUtil;
  6 import freemarker.template.TemplateException;
  7 import org.dom4j.Document;
  8 import org.dom4j.DocumentException;
  9 import org.dom4j.Node;
 10 import org.dom4j.io.SAXReader;
 11 
 12 import java.io.File;
 13 import java.io.IOException;
 14 import java.util.*;
 15 
 16 public class ServerGenerator {
 17     static String serverPath = "[module]/src/main/java/com/zihans/train/[module]/";
 18     static String pomPath = "generator\\pom.xml";
 19     static {
 20         new File(serverPath).mkdirs();
 21     }
 22 
 23     public static void main(String[] args) throws Exception {
 24         // 获取mybatis-generator
 25         String generatorPath = getGeneratorPath();
 26         // 比如generator-config-member.xml,得到module = member
 27         String module = generatorPath.replace("src/main/resources/generator-config-", "").replace(".xml", "");
 28         System.out.println("module: " + module);
 29         serverPath = serverPath.replace("[module]", module);
 30         // new File(servicePath).mkdirs();
 31         System.out.println("servicePath: " + serverPath);
 32 
 33         // 读取table节点
 34         Document document = new SAXReader().read("generator/" + generatorPath);
 35         Node table = document.selectSingleNode("//table");
 36         System.out.println(table);
 37         Node tableName = table.selectSingleNode("@tableName");
 38         Node domainObjectName = table.selectSingleNode("@domainObjectName");
 39         System.out.println(tableName.getText() + "/" + domainObjectName.getText());
 40 
 41         // 为DbUtil设置数据源
 42         Node connectionURL = document.selectSingleNode("//@connectionURL");
 43         Node userId = document.selectSingleNode("//@userId");
 44         Node password = document.selectSingleNode("//@password");
 45         System.out.println("url: " + connectionURL.getText());
 46         System.out.println("user: " + userId.getText());
 47         System.out.println("password: " + password.getText());
 48         DbUtil.url = connectionURL.getText();
 49         DbUtil.user = userId.getText();
 50         DbUtil.password = password.getText();
 51 
 52         // 示例:表名 jiawa_test
 53         // Domain = JiawaTest
 54         String Domain = domainObjectName.getText();
 55         // domain = jiawaTest
 56         String domain = Domain.substring(0, 1).toLowerCase() + Domain.substring(1);
 57         // do_main = jiawa-test
 58         String do_main = tableName.getText().replaceAll("_", "-");
 59         // 表中文名
 60         String tableNameCn = DbUtil.getTableComment(tableName.getText());
 61         List<Field> fieldList = DbUtil.getColumnByTableName(tableName.getText());
 62         Set<String> typeSet = getJavaTypes(fieldList);
 63 
 64         // 组装参数
 65         Map<String, Object> param = new HashMap<>();
 66         param.put("module", module);
 67         param.put("Domain", Domain);
 68         param.put("domain", domain);
 69         param.put("do_main", do_main);
 70         param.put("tableNameCn", tableNameCn);
 71         param.put("fieldList", fieldList);
 72         param.put("typeSet", typeSet);
 73         System.out.println("组装参数:" + param);
 74 
 75         // gen(Domain, param, "service", "service");
 76         // gen(Domain, param, "controller", "controller");
 77         // gen(Domain, param, "req", "saveReq");
 78         gen(Domain, param, "req", "queryReq");
 79         gen(Domain, param, "resp", "queryResp");
 80     }
 81 
 82     private static void gen(String Domain, Map<String, Object> param, String packageName, String target) throws IOException, TemplateException {
 83         FreemarkerUtil.initConfig(target + ".ftl");
 84         String toPath = serverPath + packageName + "/";
 85         new File(toPath).mkdirs();
 86         String Target = target.substring(0, 1).toUpperCase() + target.substring(1);
 87         String fileName = toPath + Domain + Target + ".java";
 88         System.out.println("开始生成:" + fileName);
 89         FreemarkerUtil.generator(fileName, param);
 90     }
 91 
 92     private static String getGeneratorPath() throws DocumentException {
 93         SAXReader saxReader = new SAXReader();
 94         Map<String, String> map = new HashMap<String, String>();
 95         map.put("pom", "http://maven.apache.org/POM/4.0.0");
 96         saxReader.getDocumentFactory().setXPathNamespaceURIs(map);
 97         Document document = saxReader.read(pomPath);
 98         Node node = document.selectSingleNode("//pom:configurationFile");
 99         System.out.println(node.getText());
100         return node.getText();
101     }
102 
103     /**
104      * 获取所有的Java类型,使用Set去重
105      */
106     private static Set<String> getJavaTypes(List<Field> fieldList) {
107         Set<String> set = new HashSet<>();
108         for (int i = 0; i < fieldList.size(); i++) {
109             Field field = fieldList.get(i);
110             set.add(field.getJavaType());
111         }
112         return set;
113     }
114 }
ServerGenerator.java

制作vue.ftl模板,支持只读

  1 <template>
  2   <p>
  3     <a-space>
  4       <a-button type="primary" @click="handleQuery()">刷新</a-button>
  5       <#if !readOnly><a-button type="primary" @click="onAdd">新增</a-button></#if>
  6     </a-space>
  7   </p>
  8   <a-table :dataSource="${domain}s"
  9            :columns="columns"
 10            :pagination="pagination"
 11            @change="handleTableChange"
 12            :loading="loading">
 13     <template #bodyCell="{ column, record }">
 14       <template v-if="column.dataIndex === 'operation'">
 15         <#if !readOnly>
 16         <a-space>
 17           <a-popconfirm
 18               title="删除后不可恢复,确认删除?"
 19               @confirm="onDelete(record)"
 20               ok-text="确认" cancel-text="取消">
 21             <a style="color: red">删除</a>
 22           </a-popconfirm>
 23           <a @click="onEdit(record)">编辑</a>
 24         </a-space>
 25         </#if>
 26       </template>
 27       <#list fieldList as field>
 28         <#if field.enums>
 29       <template v-else-if="column.dataIndex === '${field.nameHump}'">
 30         <span v-for="item in ${field.enumsConst}_ARRAY" :key="item.key">
 31           <span v-if="item.key === record.${field.nameHump}">
 32             {{item.value}}
 33           </span>
 34         </span>
 35       </template>
 36         </#if>
 37       </#list>
 38     </template>
 39   </a-table>
 40   <#if !readOnly>
 41   <a-modal v-model:visible="visible" title="${tableNameCn}" @ok="handleOk"
 42            ok-text="确认" cancel-text="取消">
 43     <a-form :model="${domain}" :label-col="{span: 4}" :wrapper-col="{ span: 20 }">
 44       <#list fieldList as field>
 45         <#if field.name!="id" && field.nameHump!="createTime" && field.nameHump!="updateTime">
 46       <a-form-item label="${field.nameCn}">
 47         <#if field.enums>
 48         <a-select v-model:value="${domain}.${field.nameHump}">
 49           <a-select-option v-for="item in ${field.enumsConst}_ARRAY" :key="item.key" :value="item.key">
 50             {{item.value}}
 51           </a-select-option>
 52         </a-select>
 53         <#elseif field.javaType=='Date'>
 54           <#if field.type=='time'>
 55         <a-time-picker v-model:value="${domain}.${field.nameHump}" valueFormat="HH:mm:ss" placeholder="请选择时间" />
 56           <#elseif field.type=='date'>
 57         <a-date-picker v-model:value="${domain}.${field.nameHump}" valueFormat="YYYY-MM-DD" placeholder="请选择日期" />
 58           <#else>
 59         <a-date-picker v-model:value="${domain}.${field.nameHump}" valueFormat="YYYY-MM-DD HH:mm:ss" show-time placeholder="请选择日期" />
 60           </#if>
 61         <#else>
 62         <a-input v-model:value="${domain}.${field.nameHump}" />
 63         </#if>
 64       </a-form-item>
 65         </#if>
 66       </#list>
 67     </a-form>
 68   </a-modal>
 69   </#if>
 70 </template>
 71 
 72 <script>
 73 import { defineComponent, ref, onMounted } from 'vue';
 74 import {notification} from "ant-design-vue";
 75 import axios from "axios";
 76 
 77 export default defineComponent({
 78   name: "${do_main}-view",
 79   setup() {
 80     <#list fieldList as field>
 81     <#if field.enums>
 82     const ${field.enumsConst}_ARRAY = window.${field.enumsConst}_ARRAY;
 83     </#if>
 84     </#list>
 85     const visible = ref(false);
 86     let ${domain} = ref({
 87       <#list fieldList as field>
 88       ${field.nameHump}: undefined,
 89       </#list>
 90     });
 91     const ${domain}s = ref([]);
 92     // 分页的三个属性名是固定的
 93     const pagination = ref({
 94       total: 0,
 95       current: 1,
 96       pageSize: 10,
 97     });
 98     let loading = ref(false);
 99     const columns = [
100     <#list fieldList as field>
101       <#if field.name!="id" && field.nameHump!="createTime" && field.nameHump!="updateTime">
102     {
103       title: '${field.nameCn}',
104       dataIndex: '${field.nameHump}',
105       key: '${field.nameHump}',
106     },
107       </#if>
108     </#list>
109     <#if !readOnly>
110     {
111       title: '操作',
112       dataIndex: 'operation'
113     }
114     </#if>
115     ];
116 
117     <#if !readOnly>
118     const onAdd = () => {
119       ${domain}.value = {};
120       visible.value = true;
121     };
122 
123     const onEdit = (record) => {
124       ${domain}.value = window.Tool.copy(record);
125       visible.value = true;
126     };
127 
128     const onDelete = (record) => {
129       axios.delete("/${module}/${do_main}/delete/" + record.id).then((response) => {
130         const data = response.data;
131         if (data.success) {
132           notification.success({description: "删除成功!"});
133           handleQuery({
134             page: pagination.value.current,
135             size: pagination.value.pageSize,
136           });
137         } else {
138           notification.error({description: data.message});
139         }
140       });
141     };
142 
143     const handleOk = () => {
144       axios.post("/${module}/${do_main}/save", ${domain}.value).then((response) => {
145         let data = response.data;
146         if (data.success) {
147           notification.success({description: "保存成功!"});
148           visible.value = false;
149           handleQuery({
150             page: pagination.value.current,
151             size: pagination.value.pageSize
152           });
153         } else {
154           notification.error({description: data.message});
155         }
156       });
157     };
158     </#if>
159 
160     const handleQuery = (param) => {
161       if (!param) {
162         param = {
163           page: 1,
164           size: pagination.value.pageSize
165         };
166       }
167       loading.value = true;
168       axios.get("/${module}/${do_main}/query-list", {
169         params: {
170           page: param.page,
171           size: param.size
172         }
173       }).then((response) => {
174         loading.value = false;
175         let data = response.data;
176         if (data.success) {
177           ${domain}s.value = data.content.list;
178           // 设置分页控件的值
179           pagination.value.current = param.page;
180           pagination.value.total = data.content.total;
181         } else {
182           notification.error({description: data.message});
183         }
184       });
185     };
186 
187     const handleTableChange = (pagination) => {
188       // console.log("看看自带的分页参数都有啥:" + pagination);
189       handleQuery({
190         page: pagination.current,
191         size: pagination.pageSize
192       });
193     };
194 
195     onMounted(() => {
196       handleQuery({
197         page: 1,
198         size: pagination.value.pageSize
199       });
200     });
201 
202     return {
203       <#list fieldList as field>
204       <#if field.enums>
205       ${field.enumsConst}_ARRAY,
206       </#if>
207       </#list>
208       ${domain},
209       visible,
210       ${domain}s,
211       pagination,
212       columns,
213       handleTableChange,
214       handleQuery,
215       loading,
216       <#if !readOnly>
217       onAdd,
218       handleOk,
219       onEdit,
220       onDelete
221       </#if>
222     };
223   },
224 });
225 </script>
vue.ftl
  1 package com.zihans.train.generator.server;
  2 
  3 import com.zihans.train.generator.util.DbUtil;
  4 import com.zihans.train.generator.util.Field;
  5 import com.zihans.train.generator.util.FreemarkerUtil;
  6 import freemarker.template.TemplateException;
  7 import org.dom4j.Document;
  8 import org.dom4j.DocumentException;
  9 import org.dom4j.Node;
 10 import org.dom4j.io.SAXReader;
 11 
 12 import java.io.File;
 13 import java.io.IOException;
 14 import java.util.*;
 15 
 16 public class ServerGenerator {
 17     static boolean readOnly = false;
 18     static String vuePath = "web/src/views/main/";
 19     static String serverPath = "[module]/src/main/java/com/zihans/train/[module]/";
 20     static String pomPath = "generator\\pom.xml";
 21     static {
 22         new File(serverPath).mkdirs();
 23     }
 24 
 25     public static void main(String[] args) throws Exception {
 26         // 获取mybatis-generator
 27         String generatorPath = getGeneratorPath();
 28         // 比如generator-config-member.xml,得到module = member
 29         String module = generatorPath.replace("src/main/resources/generator-config-", "").replace(".xml", "");
 30         System.out.println("module: " + module);
 31         serverPath = serverPath.replace("[module]", module);
 32         // new File(servicePath).mkdirs();
 33         System.out.println("servicePath: " + serverPath);
 34 
 35         // 读取table节点
 36         Document document = new SAXReader().read("generator/" + generatorPath);
 37         Node table = document.selectSingleNode("//table");
 38         System.out.println(table);
 39         Node tableName = table.selectSingleNode("@tableName");
 40         Node domainObjectName = table.selectSingleNode("@domainObjectName");
 41         System.out.println(tableName.getText() + "/" + domainObjectName.getText());
 42 
 43         // 为DbUtil设置数据源
 44         Node connectionURL = document.selectSingleNode("//@connectionURL");
 45         Node userId = document.selectSingleNode("//@userId");
 46         Node password = document.selectSingleNode("//@password");
 47         System.out.println("url: " + connectionURL.getText());
 48         System.out.println("user: " + userId.getText());
 49         System.out.println("password: " + password.getText());
 50         DbUtil.url = connectionURL.getText();
 51         DbUtil.user = userId.getText();
 52         DbUtil.password = password.getText();
 53 
 54         // 示例:表名 jiawa_test
 55         // Domain = JiawaTest
 56         String Domain = domainObjectName.getText();
 57         // domain = jiawaTest
 58         String domain = Domain.substring(0, 1).toLowerCase() + Domain.substring(1);
 59         // do_main = jiawa-test
 60         String do_main = tableName.getText().replaceAll("_", "-");
 61         // 表中文名
 62         String tableNameCn = DbUtil.getTableComment(tableName.getText());
 63         List<Field> fieldList = DbUtil.getColumnByTableName(tableName.getText());
 64         Set<String> typeSet = getJavaTypes(fieldList);
 65 
 66         // 组装参数
 67         Map<String, Object> param = new HashMap<>();
 68         param.put("module", module);
 69         param.put("Domain", Domain);
 70         param.put("domain", domain);
 71         param.put("do_main", do_main);
 72         param.put("tableNameCn", tableNameCn);
 73         param.put("fieldList", fieldList);
 74         param.put("typeSet", typeSet);
 75         param.put("readOnly", readOnly);
 76         System.out.println("组装参数:" + param);
 77 
 78         // gen(Domain, param, "service", "service");
 79         // gen(Domain, param, "controller", "controller");
 80         // gen(Domain, param, "req", "saveReq");
 81         // gen(Domain, param, "req", "queryReq");
 82         // gen(Domain, param, "resp", "queryResp");
 83 
 84         genVue(do_main, param);
 85     }
 86 
 87     private static void gen(String Domain, Map<String, Object> param, String packageName, String target) throws IOException, TemplateException {
 88         FreemarkerUtil.initConfig(target + ".ftl");
 89         String toPath = serverPath + packageName + "/";
 90         new File(toPath).mkdirs();
 91         String Target = target.substring(0, 1).toUpperCase() + target.substring(1);
 92         String fileName = toPath + Domain + Target + ".java";
 93         System.out.println("开始生成:" + fileName);
 94         FreemarkerUtil.generator(fileName, param);
 95     }
 96 
 97     private static void genVue(String do_main, Map<String, Object> param) throws IOException, TemplateException {
 98         FreemarkerUtil.initConfig("vue.ftl");
 99         new File(vuePath).mkdirs();
100         String fileName = vuePath + do_main + ".vue";
101         System.out.println("开始生成:" + fileName);
102         FreemarkerUtil.generator(fileName, param);
103     }
104 
105     private static String getGeneratorPath() throws DocumentException {
106         SAXReader saxReader = new SAXReader();
107         Map<String, String> map = new HashMap<String, String>();
108         map.put("pom", "http://maven.apache.org/POM/4.0.0");
109         saxReader.getDocumentFactory().setXPathNamespaceURIs(map);
110         Document document = saxReader.read(pomPath);
111         Node node = document.selectSingleNode("//pom:configurationFile");
112         System.out.println(node.getText());
113         return node.getText();
114     }
115 
116     /**
117      * 获取所有的Java类型,使用Set去重
118      */
119     private static Set<String> getJavaTypes(List<Field> fieldList) {
120         Set<String> set = new HashSet<>();
121         for (int i = 0; i < fieldList.size(); i++) {
122             Field field = fieldList.get(i);
123             set.add(field.getJavaType());
124         }
125         return set;
126     }
127 }
ServerGenerator.java

修改前端枚举,将key/value改成code/desc,保持和后端一致

  1 <template>
  2   <p>
  3     <a-space>
  4       <a-button type="primary" @click="handleQuery()">刷新</a-button>
  5       <#if !readOnly><a-button type="primary" @click="onAdd">新增</a-button></#if>
  6     </a-space>
  7   </p>
  8   <a-table :dataSource="${domain}s"
  9            :columns="columns"
 10            :pagination="pagination"
 11            @change="handleTableChange"
 12            :loading="loading">
 13     <template #bodyCell="{ column, record }">
 14       <template v-if="column.dataIndex === 'operation'">
 15         <#if !readOnly>
 16         <a-space>
 17           <a-popconfirm
 18               title="删除后不可恢复,确认删除?"
 19               @confirm="onDelete(record)"
 20               ok-text="确认" cancel-text="取消">
 21             <a style="color: red">删除</a>
 22           </a-popconfirm>
 23           <a @click="onEdit(record)">编辑</a>
 24         </a-space>
 25         </#if>
 26       </template>
 27       <#list fieldList as field>
 28         <#if field.enums>
 29       <template v-else-if="column.dataIndex === '${field.nameHump}'">
 30         <span v-for="item in ${field.enumsConst}_ARRAY" :key="item.code">
 31           <span v-if="item.code === record.${field.nameHump}">
 32             {{item.desc}}
 33           </span>
 34         </span>
 35       </template>
 36         </#if>
 37       </#list>
 38     </template>
 39   </a-table>
 40   <#if !readOnly>
 41   <a-modal v-model:visible="visible" title="${tableNameCn}" @ok="handleOk"
 42            ok-text="确认" cancel-text="取消">
 43     <a-form :model="${domain}" :label-col="{span: 4}" :wrapper-col="{ span: 20 }">
 44       <#list fieldList as field>
 45         <#if field.name!="id" && field.nameHump!="createTime" && field.nameHump!="updateTime">
 46       <a-form-item label="${field.nameCn}">
 47         <#if field.enums>
 48         <a-select v-model:value="${domain}.${field.nameHump}">
 49           <a-select-option v-for="item in ${field.enumsConst}_ARRAY" :key="item.code" :value="item.code">
 50             {{item.desc}}
 51           </a-select-option>
 52         </a-select>
 53         <#elseif field.javaType=='Date'>
 54           <#if field.type=='time'>
 55         <a-time-picker v-model:value="${domain}.${field.nameHump}" valueFormat="HH:mm:ss" placeholder="请选择时间" />
 56           <#elseif field.type=='date'>
 57         <a-date-picker v-model:value="${domain}.${field.nameHump}" valueFormat="YYYY-MM-DD" placeholder="请选择日期" />
 58           <#else>
 59         <a-date-picker v-model:value="${domain}.${field.nameHump}" valueFormat="YYYY-MM-DD HH:mm:ss" show-time placeholder="请选择日期" />
 60           </#if>
 61         <#else>
 62         <a-input v-model:value="${domain}.${field.nameHump}" />
 63         </#if>
 64       </a-form-item>
 65         </#if>
 66       </#list>
 67     </a-form>
 68   </a-modal>
 69   </#if>
 70 </template>
 71 
 72 <script>
 73 import { defineComponent, ref, onMounted } from 'vue';
 74 import {notification} from "ant-design-vue";
 75 import axios from "axios";
 76 
 77 export default defineComponent({
 78   name: "${do_main}-view",
 79   setup() {
 80     <#list fieldList as field>
 81     <#if field.enums>
 82     const ${field.enumsConst}_ARRAY = window.${field.enumsConst}_ARRAY;
 83     </#if>
 84     </#list>
 85     const visible = ref(false);
 86     let ${domain} = ref({
 87       <#list fieldList as field>
 88       ${field.nameHump}: undefined,
 89       </#list>
 90     });
 91     const ${domain}s = ref([]);
 92     // 分页的三个属性名是固定的
 93     const pagination = ref({
 94       total: 0,
 95       current: 1,
 96       pageSize: 10,
 97     });
 98     let loading = ref(false);
 99     const columns = [
100     <#list fieldList as field>
101       <#if field.name!="id" && field.nameHump!="createTime" && field.nameHump!="updateTime">
102     {
103       title: '${field.nameCn}',
104       dataIndex: '${field.nameHump}',
105       key: '${field.nameHump}',
106     },
107       </#if>
108     </#list>
109     <#if !readOnly>
110     {
111       title: '操作',
112       dataIndex: 'operation'
113     }
114     </#if>
115     ];
116 
117     <#if !readOnly>
118     const onAdd = () => {
119       ${domain}.value = {};
120       visible.value = true;
121     };
122 
123     const onEdit = (record) => {
124       ${domain}.value = window.Tool.copy(record);
125       visible.value = true;
126     };
127 
128     const onDelete = (record) => {
129       axios.delete("/${module}/${do_main}/delete/" + record.id).then((response) => {
130         const data = response.data;
131         if (data.success) {
132           notification.success({description: "删除成功!"});
133           handleQuery({
134             page: pagination.value.current,
135             size: pagination.value.pageSize,
136           });
137         } else {
138           notification.error({description: data.message});
139         }
140       });
141     };
142 
143     const handleOk = () => {
144       axios.post("/${module}/${do_main}/save", ${domain}.value).then((response) => {
145         let data = response.data;
146         if (data.success) {
147           notification.success({description: "保存成功!"});
148           visible.value = false;
149           handleQuery({
150             page: pagination.value.current,
151             size: pagination.value.pageSize
152           });
153         } else {
154           notification.error({description: data.message});
155         }
156       });
157     };
158     </#if>
159 
160     const handleQuery = (param) => {
161       if (!param) {
162         param = {
163           page: 1,
164           size: pagination.value.pageSize
165         };
166       }
167       loading.value = true;
168       axios.get("/${module}/${do_main}/query-list", {
169         params: {
170           page: param.page,
171           size: param.size
172         }
173       }).then((response) => {
174         loading.value = false;
175         let data = response.data;
176         if (data.success) {
177           ${domain}s.value = data.content.list;
178           // 设置分页控件的值
179           pagination.value.current = param.page;
180           pagination.value.total = data.content.total;
181         } else {
182           notification.error({description: data.message});
183         }
184       });
185     };
186 
187     const handleTableChange = (pagination) => {
188       // console.log("看看自带的分页参数都有啥:" + pagination);
189       handleQuery({
190         page: pagination.current,
191         size: pagination.pageSize
192       });
193     };
194 
195     onMounted(() => {
196       handleQuery({
197         page: 1,
198         size: pagination.value.pageSize
199       });
200     });
201 
202     return {
203       <#list fieldList as field>
204       <#if field.enums>
205       ${field.enumsConst}_ARRAY,
206       </#if>
207       </#list>
208       ${domain},
209       visible,
210       ${domain}s,
211       pagination,
212       columns,
213       handleTableChange,
214       handleQuery,
215       loading,
216       <#if !readOnly>
217       onAdd,
218       handleOk,
219       onEdit,
220       onDelete
221       </#if>
222     };
223   },
224 });
225 </script>
vue.ftl

增加枚举生成器EnumGenerator.java

 1 package com.zihans.train.generator.gen;
 2 
 3 import cn.hutool.core.util.StrUtil;
 4 import com.zihans.train.member.enums.PassengerTypeEnum;
 5 
 6 import java.io.FileOutputStream;
 7 import java.io.OutputStreamWriter;
 8 import java.lang.reflect.Method;
 9 
10 public class EnumGenerator {
11     static String path = "web/src/assets/js/enums.js";
12 
13     public static void main(String[] args) {
14         StringBuffer bufferObject = new StringBuffer();
15         StringBuffer bufferArray = new StringBuffer();
16         long begin = System.currentTimeMillis();
17         try {
18             toJson(PassengerTypeEnum.class, bufferObject, bufferArray);
19 
20             StringBuffer buffer = bufferObject.append("\r\n").append(bufferArray);
21             writeJs(buffer);
22         } catch (Exception e) {
23             e.printStackTrace();
24         }
25         long end = System.currentTimeMillis();
26         System.out.println("执行耗时:" + (end - begin) + " 毫秒");
27     }
28 
29     private static void toJson(Class clazz, StringBuffer bufferObject, StringBuffer bufferArray) throws Exception {
30         // enumConst:将YesNoEnum变成YES_NO
31         String enumConst = StrUtil.toUnderlineCase(clazz.getSimpleName())
32                 .toUpperCase().replace("_ENUM", "");
33         Object[] objects = clazz.getEnumConstants();
34         Method name = clazz.getMethod("name");
35         Method getDesc = clazz.getMethod("getDesc");
36         Method getCode = clazz.getMethod("getCode");
37 
38         // 生成对象
39         bufferObject.append(enumConst).append("={");
40         for (int i = 0; i < objects.length; i++) {
41             Object obj = objects[i];
42             bufferObject.append(name.invoke(obj)).append(":{code:\"").append(getCode.invoke(obj)).append("\", desc:\"").append(getDesc.invoke(obj)).append("\"}");
43             if (i < objects.length - 1) {
44                 bufferObject.append(",");
45             }
46         }
47         bufferObject.append("};\r\n");
48 
49         // 生成数组
50         bufferArray.append(enumConst).append("_ARRAY=[");
51         for (int i = 0; i < objects.length; i++) {
52             Object obj = objects[i];
53             bufferArray.append("{code:\"").append(getCode.invoke(obj)).append("\", desc:\"").append(getDesc.invoke(obj)).append("\"}");
54             if (i < objects.length - 1) {
55                 bufferArray.append(",");
56             }
57         }
58         bufferArray.append("];\r\n");
59     }
60 
61     /**
62      * 写文件
63      * @param stringBuffer
64      */
65     public static void writeJs(StringBuffer stringBuffer) {
66         FileOutputStream out = null;
67         try {
68             out = new FileOutputStream(path);
69             OutputStreamWriter osw = new OutputStreamWriter(out, "UTF-8");
70             System.out.println(path);
71             osw.write(stringBuffer.toString());
72             osw.close();
73         } catch (Exception e) {
74             e.printStackTrace();
75         }
76         finally {
77             try {
78                 out.close();
79             } catch (Exception e) {
80                 e.printStackTrace();
81             }
82 
83         }
84     }
85 
86 }
EnumGenerator.java