thymeleaf+mybatis代码生成

2025-04-09 · 143 次阅读 ·

前言

最近在开发一款个人小程序,介于可能使用的人可能不是很多,就采用了springboot+mybatis单服务架构作为后端,在代码生成模块使用了mybatis-generator但是繁琐的配置,不能简易自定义都成了痛点。于是使用thymeleaf模板实现项目代码生成。

原理也很简单,从数据库解析表结构。根据字段类型,渲染模板文件。创建实体类+mapper.xml+Mapper.java

步骤

1创建生成模板

创建pojoTemplate.txt

代码
  1. 1package [(${daoPackage})];
  2. 2
  3. 3import [(${pojoPackage})].[(${table.javaTableName})];
  4. 4import org.apache.ibatis.annotations.Mapper;
  5. 5
  6. 6@Mapper
  7. 7public interface [(${table.javaTableName})]Mapper extends BaseMapper<[(${table.javaTableName})]> {
  8. 8
  9. 9}

创建mapperTemplate.txt

代码
  1. 1<?xml version="1.0" encoding="UTF-8" ?>
  2. 2<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
  3. 3<mapper namespace="[(${daoPackage})].[(${table.javaTableName})]Mapper" >
  4. 4 <resultMap id="BaseResultMap" type="[(${pojoPackage})].[(${table.javaTableName})]" >
  5. 5 [# th:each="field : ${table.fieldList}"][# th:if="${field.isPrimaryKey}"] <id column="[(${field.name})]" property="[(${field.camelName})]" jdbcType="[(${field.jdbcType})]" />
  6. 6 [/][# th:if="${!field.isPrimaryKey}"] <result column="[(${field.name})]" property="[(${field.camelName})]" jdbcType="[(${field.jdbcType})]" />
  7. 7 [/][/]</resultMap>
  8. 8 <sql id="Base_Column_List" >
  9. 9 [# th:each="field, var : ${table.fieldList}"][(${field.name})][# th:if="${!var.last}"],[/][/]
  10. 10 </sql>
  11. 11 <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.[(${table.primaryKey.javaType})]" >
  12. 12 select
  13. 13 <include refid="Base_Column_List" />
  14. 14 from [(${table.tableName})]
  15. 15 where [(${table.primaryKey.name})] = #{[(${table.primaryKey.camelName})],jdbcType=[(${table.primaryKey.jdbcType})]}
  16. 16 </select>
  17. 17 <delete id="deleteByPrimaryKey" parameterType="java.lang.[(${table.primaryKey.javaType})]" >
  18. 18 delete from [(${table.tableName})]
  19. 19 where [(${table.primaryKey.name})] = #{[(${table.primaryKey.camelName})],jdbcType=[(${table.primaryKey.jdbcType})]}
  20. 20 </delete>
  21. 21 <insert id="insert" parameterType="life.wqg.pojo.SysJob" useGeneratedKeys="true" keyProperty="[(${table.primaryKey.camelName})]">
  22. 22 insert into [(${table.tableName})] ([# th:each="field, var : ${table.fieldList}"][(${field.name})][# th:if="${!var.last}"],[/][/])
  23. 23 values ([# th:each="field, var : ${table.fieldList}"]#{[(${field.camelName})],jdbcType=[(${field.jdbcType})]}[# th:if="${!var.last}"],[/][/])
  24. 24 </insert>
  25. 25 <insert id="insertSelective" parameterType="life.wqg.pojo.SysJob" useGeneratedKeys="true" keyProperty="[(${table.primaryKey.camelName})]">
  26. 26 insert into [(${table.tableName})]
  27. 27 <trim prefix="(" suffix=")" suffixOverrides="," >
  28. 28 [# th:each="field, var : ${table.fieldList}"] <if test="[(${field.camelName})] != null" >[(${field.name})],</if>
  29. 29 [/]</trim>
  30. 30 <trim prefix="values (" suffix=")" suffixOverrides="," >
  31. 31 [# th:each="field, var : ${table.fieldList}"] <if test="[(${field.camelName})] != null" >#{[(${field.camelName})],jdbcType=[(${field.jdbcType})]},</if>
  32. 32 [/]</trim>
  33. 33 </insert>
  34. 34 <update id="updateByPrimaryKeySelective" parameterType="[(${pojoPackage})].[(${table.javaTableName})]" >
  35. 35 update [(${table.tableName})]
  36. 36 <set >
  37. 37 [# th:each="field, var : ${table.fieldList}"][# th:if="${!field.isPrimaryKey}"] <if test="[(${field.camelName})] != null" >[(${field.name})] = #{[(${field.camelName})],jdbcType=[(${field.jdbcType})]},</if>
  38. 38 [/][/]</set>
  39. 39 where [(${table.primaryKey.name})] = #{[(${table.primaryKey.camelName})],jdbcType=[(${table.primaryKey.jdbcType})]}
  40. 40 </update>
  41. 41 <update id="updateByPrimaryKey" parameterType="[(${pojoPackage})].[(${table.javaTableName})]" >
  42. 42 update [(${table.tableName})]
  43. 43 set
  44. 44 [# th:each="field, var : ${table.fieldList}"][# th:if="${!field.isPrimaryKey}"] [(${field.name})] = #{[(${field.camelName})],jdbcType=[(${field.jdbcType})]}[# th:if="${!var.last}"],[/]
  45. 45 [/][/]where [(${table.primaryKey.name})] = #{[(${table.primaryKey.camelName})],jdbcType=[(${table.primaryKey.jdbcType})]}
  46. 46 </update>
  47. 47</mapper>

创建daoTemplate.txt

代码
  1. 1package [(${daoPackage})];
  2. 2
  3. 3import [(${pojoPackage})].[(${table.javaTableName})];
  4. 4import org.apache.ibatis.annotations.Mapper;
  5. 5
  6. 6@Mapper
  7. 7public interface [(${table.javaTableName})]Mapper extends BaseMapper<[(${table.javaTableName})]> {
  8. 8
  9. 9}

2创建表实体

创建实体SqlTable.java

代码
  1. 1package life.wqg.common.utils.code.entity;
  2. 2
  3. 3import java.util.List;
  4. 4
  5. 5public class SqlTable {
  6. 6 /** 表名 */
  7. 7 private String tableName;
  8. 8
  9. 9
  10. 10 /** 对应的java实体类的名称 */
  11. 11 private String javaTableName;
  12. 12
  13. 13 /** 表里面的字段名 */
  14. 14 private List<SqlField> fieldList;
  15. 15
  16. 16 /** 表里面的主键 */
  17. 17 private SqlField primaryKey;
  18. 18
  19. 19 //省略getter setter
  20. 20}

创建实体SqlField.java

代码
  1. 1package life.wqg.common.utils.code.entity;
  2. 2
  3. 3public class SqlField {
  4. 4 /**字段名称*/
  5. 5 private String name;
  6. 6
  7. 7 /**驼蜂式命名*/
  8. 8 private String camelName;
  9. 9
  10. 10 /**首字母大写的驼蜂式命名*/
  11. 11 private String firstUpCamelName;
  12. 12
  13. 13 /**字段类型*/
  14. 14 private String type;
  15. 15
  16. 16
  17. 17 /**java实体类的类型*/
  18. 18 private String javaType;
  19. 19
  20. 20
  21. 21 /**备注*/
  22. 22 private String remark;
  23. 23
  24. 24
  25. 25 /** 数据库类型 */
  26. 26 private String jdbcType;
  27. 27
  28. 28 /** 是否为主键 */
  29. 29 private Boolean isPrimaryKey;
  30. 30
  31. 31
  32. 32 //省略getter setter
  33. 33}

3创建生成工具类

代码
  1. 1package life.wqg.common.utils.code;
  2. 2
  3. 3import cn.hutool.core.io.IORuntimeException;
  4. 4import cn.hutool.core.io.file.FileReader;
  5. 5import cn.hutool.core.io.file.FileWriter;
  6. 6import cn.hutool.core.util.StrUtil;
  7. 7import life.wqg.common.utils.code.entity.SqlField;
  8. 8import life.wqg.common.utils.code.entity.SqlTable;
  9. 9import org.thymeleaf.TemplateEngine;
  10. 10import org.thymeleaf.context.Context;
  11. 11
  12. 12import javax.sql.DataSource;
  13. 13import java.io.File;
  14. 14import java.sql.Connection;
  15. 15import java.sql.DatabaseMetaData;
  16. 16import java.sql.ResultSet;
  17. 17import java.sql.SQLException;
  18. 18import java.util.*;
  19. 19import java.util.regex.Matcher;
  20. 20
  21. 21/**
  22. 22 * 代码生成器
  23. 23 */
  24. 24public class CodeGenerator {
  25. 25
  26. 26
  27. 27 private Map<String, String> property2JavaMap = new HashMap<>();
  28. 28
  29. 29 private Map<String, String> property2JdbcMap = new HashMap<>();
  30. 30
  31. 31 private DataSource dataSource = null;
  32. 32
  33. 33 private TemplateEngine templateEngine = null;
  34. 34
  35. 35 private String filePath = "";
  36. 36
  37. 37 private String pojoPackage = "";
  38. 38
  39. 39 private String daoPackage = "";
  40. 40
  41. 41 private String mapperPackage = "";
  42. 42
  43. 43 public CodeGenerator(DataSource dataSource, TemplateEngine templateEngine, String pojoPackage, String daoPackage, String mapperPackage) {
  44. 44 this.dataSource = dataSource;
  45. 45 this.templateEngine = templateEngine;
  46. 46 this.filePath = System.getProperty("user.dir");
  47. 47 this.pojoPackage = pojoPackage;
  48. 48 this.daoPackage = daoPackage;
  49. 49 this.mapperPackage = mapperPackage;
  50. 50 property2JavaMap.put("BIGINT UNSIGNED", "Long");
  51. 51 property2JavaMap.put("DATETIME", "Date");
  52. 52 property2JavaMap.put("TIMESTAMP", "Date");
  53. 53 property2JavaMap.put("VARCHAR", "String");
  54. 54 property2JavaMap.put("DECIMAL", "BigDecimal");
  55. 55 property2JavaMap.put("BIGINT", "Long");
  56. 56 property2JavaMap.put("TEXT", "String");
  57. 57 property2JavaMap.put("TINYINT", "Integer");
  58. 58 property2JavaMap.put("INT", "Integer");
  59. 59 property2JavaMap.put("BIT", "Integer");
  60. 60 property2JavaMap.put("CHAR", "String");
  61. 61
  62. 62 property2JdbcMap.put("BIGINT UNSIGNED", "BIGINT");
  63. 63 property2JdbcMap.put("DATETIME", "TIMESTAMP");
  64. 64 property2JdbcMap.put("TIMESTAMP", "TIMESTAMP");
  65. 65 property2JdbcMap.put("VARCHAR", "VARCHAR");
  66. 66 property2JdbcMap.put("DECIMAL", "DECIMAL");
  67. 67 property2JdbcMap.put("BIGINT", "BIGINT");
  68. 68 property2JdbcMap.put("TEXT", "VARCHAR");
  69. 69 property2JdbcMap.put("TINYINT", "TINYINT");
  70. 70 property2JdbcMap.put("INT", "INTEGER");
  71. 71 property2JdbcMap.put("BIT", "BIT");
  72. 72 property2JdbcMap.put("CHAR", "CHAR");
  73. 73 }
  74. 74
  75. 75 public SqlTable getTableInfo(String dbName, String tableName) {
  76. 76
  77. 77
  78. 78 SqlTable table = new SqlTable();
  79. 79 //把tableName转成驼峰式,并且首子母改成大写
  80. 80 String javaTableName = StrUtil.upperFirst(StrUtil.toCamelCase(tableName));
  81. 81 table.setTableName(tableName);
  82. 82 table.setJavaTableName(javaTableName);
  83. 83 Connection conn = null;
  84. 84 try {
  85. 85 conn = dataSource.getConnection();
  86. 86
  87. 87 DatabaseMetaData metaData = conn.getMetaData();
  88. 88 ResultSet resultSet = metaData.getColumns(null, null, tableName, "%");
  89. 89 ResultSet pkRSet = metaData.getPrimaryKeys(null, null, tableName);
  90. 90 Set<String> pkSet = new HashSet<>();
  91. 91 while (pkRSet.next()) {
  92. 92 pkSet.add(pkRSet.getString("COLUMN_NAME"));
  93. 93 }
  94. 94 String columnName;
  95. 95 String columnType;
  96. 96
  97. 97
  98. 98 List<SqlField> fieldList = new ArrayList<>();
  99. 99 while (resultSet.next()) {
  100. 100 if (!resultSet.getString("TABLE_CAT").equals(dbName)) {
  101. 101 continue;
  102. 102 }
  103. 103 SqlField field = new SqlField();
  104. 104 columnName = resultSet.getString("COLUMN_NAME");
  105. 105 columnType = resultSet.getString("TYPE_NAME");
  106. 106 String remark = resultSet.getString("REMARKS");
  107. 107 field.setName(columnName);
  108. 108 field.setIsPrimaryKey(pkSet.contains(columnName));
  109. 109 field.setRemark(remark);
  110. 110 field.setType(columnType);
  111. 111 field.setCamelName(StrUtil.toCamelCase(columnName));
  112. 112 field.setFirstUpCamelName(StrUtil.upperFirst(field.getCamelName()));
  113. 113 field.setJavaType(property2JavaMap.get(columnType));
  114. 114 field.setJdbcType(property2JdbcMap.get(columnType));
  115. 115 fieldList.add(field);
  116. 116 if (pkSet.contains(columnName)){
  117. 117 //主键
  118. 118 table.setPrimaryKey(field);
  119. 119 }
  120. 120 }
  121. 121 table.setFieldList(fieldList);
  122. 122 } catch (SQLException ex) {
  123. 123 ex.printStackTrace();
  124. 124 }
  125. 125 return table;
  126. 126 }
  127. 127
  128. 128
  129. 129 /**
  130. 130 * 开始生成代码
  131. 131 */
  132. 132 public void startGenerate(String dbName, String tableName, boolean isGeneratePojo, boolean isGenerateDao, boolean isGenerateXml,boolean isOverwriteDao) {
  133. 133 SqlTable tableInfo = this.getTableInfo(dbName, tableName);
  134. 134 Context ctx = new Context(Locale.CHINA);
  135. 135 if (isGeneratePojo) {
  136. 136 generatePojo(ctx, tableInfo);
  137. 137 }
  138. 138 if (isGenerateDao) {
  139. 139 generateDao(ctx, tableInfo,isOverwriteDao);
  140. 140 }
  141. 141 if (isGenerateXml) {
  142. 142 generateXml(ctx,tableInfo);
  143. 143 }
  144. 144 }
  145. 145
  146. 146 /**
  147. 147 * 生成xml
  148. 148 * @param ctx
  149. 149 * @param tableInfo
  150. 150 */
  151. 151 private void generateXml(Context ctx, SqlTable tableInfo) {
  152. 152 Map<String, Object> map = new HashMap<>();
  153. 153 map.put("table", tableInfo);
  154. 154 map.put("pojoPackage", pojoPackage);
  155. 155 map.put("daoPackage", daoPackage);
  156. 156 ctx.setVariables(map);
  157. 157 String process = templateEngine.process("code/mapperTemplate.txt", ctx);
  158. 158 FileWriter writer = new FileWriter(filePath + "\\src\\main\\resources\\" + mapperPackage.replaceAll("\\.", Matcher.quoteReplacement(File.separator)) + "\\" + tableInfo.getJavaTableName() + "Mapper.xml");
  159. 159 writer.write(process);
  160. 160 }
  161. 161
  162. 162 /**
  163. 163 * 生成Dao
  164. 164 *
  165. 165 * @param ctx
  166. 166 * @param tableInfo
  167. 167 */
  168. 168 private void generateDao(Context ctx, SqlTable tableInfo,boolean isOverwriteDao) {
  169. 169 if (!isOverwriteDao){
  170. 170 try {
  171. 171 FileReader fileReader = new FileReader(filePath + "\\src\\main\\java\\" + daoPackage.replaceAll("\\.", Matcher.quoteReplacement(File.separator)) + "\\" + tableInfo.getJavaTableName() + "Mapper.java");
  172. 172 String result = fileReader.readString();
  173. 173 if (!StrUtil.isEmpty(result)){
  174. 174 return;
  175. 175 }
  176. 176 }catch (IORuntimeException exception){
  177. 177 System.out.println("文件不存在,已生成");
  178. 178 }
  179. 179
  180. 180 }
  181. 181 Map<String, Object> map = new HashMap<>();
  182. 182 map.put("table", tableInfo);
  183. 183 map.put("pojoPackage", pojoPackage);
  184. 184 map.put("daoPackage", daoPackage);
  185. 185 ctx.setVariables(map);
  186. 186 String process = templateEngine.process("code/daoTemplate.txt", ctx);
  187. 187 FileWriter writer = null;
  188. 188 writer = new FileWriter(filePath + "\\src\\main\\java\\" + daoPackage.replaceAll("\\.", Matcher.quoteReplacement(File.separator)) + "\\" + tableInfo.getJavaTableName() + "Mapper.java");
  189. 189 writer.write(process);
  190. 190 }
  191. 191
  192. 192 /**
  193. 193 * 生成pojo
  194. 194 *
  195. 195 * @param ctx
  196. 196 */
  197. 197 private void generatePojo(Context ctx, SqlTable tableInfo) {
  198. 198 Map<String, Object> map = new HashMap<>();
  199. 199 map.put("table", tableInfo);
  200. 200 map.put("pojoPackage", pojoPackage);
  201. 201 ctx.setVariables(map);
  202. 202 String process = templateEngine.process("code/pojoTemplate.txt", ctx);
  203. 203 FileWriter writer = null;
  204. 204 writer = new FileWriter(filePath + "\\src\\main\\java\\" + pojoPackage.replaceAll("\\.", Matcher.quoteReplacement(File.separator)) + "\\" + tableInfo.getJavaTableName() + ".java");
  205. 205 writer.write(process);
  206. 206 }
  207. 207
  208. 208
  209. 209 public static void main(String[] args) {
  210. 210 System.out.printf(System.getProperty("user.dir"));
  211. 211 /* FileWriter writer = new FileWriter("test.properties");
  212. 212 writer.write("test");*/
  213. 213 }
  214. 214
  215. 215}

4调用生成

代码
  1. 1package life.wqg;
  2. 2
  3. 3import com.upyun.UpException;
  4. 4import life.wqg.common.utils.code.CodeGenerator;
  5. 5import org.springframework.beans.factory.annotation.Autowired;
  6. 6import org.springframework.boot.test.context.SpringBootTest;
  7. 7import org.thymeleaf.TemplateEngine;
  8. 8import org.thymeleaf.templatemode.TemplateMode;
  9. 9import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver;
  10. 10
  11. 11import javax.sql.DataSource;
  12. 12import java.io.IOException;
  13. 13import java.sql.SQLException;
  14. 14import java.util.Collections;
  15. 15
  16. 16@SpringBootTest
  17. 17public class GeneratorTest {
  18. 18 @Autowired
  19. 19 private DataSource dataSource;
  20. 20
  21. 21 @Autowired
  22. 22 private TemplateEngine templateEngine;
  23. 23
  24. 24 @org.junit.jupiter.api.Test
  25. 25 public void generator() throws SQLException, UpException, IOException {
  26. 26 String dbName = "test";
  27. 27 CodeGenerator codeGenerator = new CodeGenerator(dataSource,templateEngine,"life.wqg.pojo","life.wqg.dao","mapper");
  28. 28 codeGenerator.startGenerate(dbName,"user_info",true,true,true,false);
  29. 29
  30. 30 }
  31. 31}

评论

还没有评论。