五、自制代码生成器提高开发效率
主要内容
- 以乘车人增删改查为模板,自制单表管理,前后端生成器。
- 学习代码生成器原理,学习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 }
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 }
运行后生成
1 package com.zihans.train.generator.test; 2 3 public class Test1 { 4 5 private String name; 6 }
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>
读取并输出上面的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 }
集成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 }
使用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 }
生成器
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 }
使用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 }
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 }
制作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 }
再写一个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 }
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 }
详解实体类生成器
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 }
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 }
按模板生成代码
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 }
制作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 }
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 }
制作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>
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 }
修改前端枚举,将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>
增加枚举生成器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 }