thymeleaf+mybatis代码生成
2025-04-09 · 143 次阅读 · WQG
前言
最近在开发一款个人小程序,介于可能使用的人可能不是很多,就采用了springboot+mybatis单服务架构作为后端,在代码生成模块使用了mybatis-generator但是繁琐的配置,不能简易自定义都成了痛点。于是使用thymeleaf模板实现项目代码生成。
原理也很简单,从数据库解析表结构。根据字段类型,渲染模板文件。创建实体类+mapper.xml+Mapper.java
步骤
1创建生成模板
创建pojoTemplate.txt
- 1
package [(${daoPackage})]; - 2
- 3
import [(${pojoPackage})].[(${table.javaTableName})]; - 4
import org.apache.ibatis.annotations.Mapper; - 5
- 6
@Mapper - 7
public interface [(${table.javaTableName})]Mapper extends BaseMapper<[(${table.javaTableName})]> { - 8
- 9
}
创建mapperTemplate.txt
- 1
<?xml version="1.0" encoding="UTF-8" ?> - 2
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > - 3
<mapper namespace="[(${daoPackage})].[(${table.javaTableName})]Mapper" > - 4
<resultMap id="BaseResultMap" type="[(${pojoPackage})].[(${table.javaTableName})]" > - 5
[# th:each="field : ${table.fieldList}"][# th:if="${field.isPrimaryKey}"] <id column="[(${field.name})]" property="[(${field.camelName})]" jdbcType="[(${field.jdbcType})]" /> - 6
[/][# th:if="${!field.isPrimaryKey}"] <result column="[(${field.name})]" property="[(${field.camelName})]" jdbcType="[(${field.jdbcType})]" /> - 7
[/][/]</resultMap> - 8
<sql id="Base_Column_List" > - 9
[# th:each="field, var : ${table.fieldList}"][(${field.name})][# th:if="${!var.last}"],[/][/] - 10
</sql> - 11
<select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.[(${table.primaryKey.javaType})]" > - 12
select - 13
<include refid="Base_Column_List" /> - 14
from [(${table.tableName})] - 15
where [(${table.primaryKey.name})] = #{[(${table.primaryKey.camelName})],jdbcType=[(${table.primaryKey.jdbcType})]} - 16
</select> - 17
<delete id="deleteByPrimaryKey" parameterType="java.lang.[(${table.primaryKey.javaType})]" > - 18
delete from [(${table.tableName})] - 19
where [(${table.primaryKey.name})] = #{[(${table.primaryKey.camelName})],jdbcType=[(${table.primaryKey.jdbcType})]} - 20
</delete> - 21
<insert id="insert" parameterType="life.wqg.pojo.SysJob" useGeneratedKeys="true" keyProperty="[(${table.primaryKey.camelName})]"> - 22
insert into [(${table.tableName})] ([# th:each="field, var : ${table.fieldList}"][(${field.name})][# th:if="${!var.last}"],[/][/]) - 23
values ([# th:each="field, var : ${table.fieldList}"]#{[(${field.camelName})],jdbcType=[(${field.jdbcType})]}[# th:if="${!var.last}"],[/][/]) - 24
</insert> - 25
<insert id="insertSelective" parameterType="life.wqg.pojo.SysJob" useGeneratedKeys="true" keyProperty="[(${table.primaryKey.camelName})]"> - 26
insert into [(${table.tableName})] - 27
<trim prefix="(" suffix=")" suffixOverrides="," > - 28
[# th:each="field, var : ${table.fieldList}"] <if test="[(${field.camelName})] != null" >[(${field.name})],</if> - 29
[/]</trim> - 30
<trim prefix="values (" suffix=")" suffixOverrides="," > - 31
[# th:each="field, var : ${table.fieldList}"] <if test="[(${field.camelName})] != null" >#{[(${field.camelName})],jdbcType=[(${field.jdbcType})]},</if> - 32
[/]</trim> - 33
</insert> - 34
<update id="updateByPrimaryKeySelective" parameterType="[(${pojoPackage})].[(${table.javaTableName})]" > - 35
update [(${table.tableName})] - 36
<set > - 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
[/][/]</set> - 39
where [(${table.primaryKey.name})] = #{[(${table.primaryKey.camelName})],jdbcType=[(${table.primaryKey.jdbcType})]} - 40
</update> - 41
<update id="updateByPrimaryKey" parameterType="[(${pojoPackage})].[(${table.javaTableName})]" > - 42
update [(${table.tableName})] - 43
set - 44
[# th:each="field, var : ${table.fieldList}"][# th:if="${!field.isPrimaryKey}"] [(${field.name})] = #{[(${field.camelName})],jdbcType=[(${field.jdbcType})]}[# th:if="${!var.last}"],[/] - 45
[/][/]where [(${table.primaryKey.name})] = #{[(${table.primaryKey.camelName})],jdbcType=[(${table.primaryKey.jdbcType})]} - 46
</update> - 47
</mapper>
创建daoTemplate.txt
- 1
package [(${daoPackage})]; - 2
- 3
import [(${pojoPackage})].[(${table.javaTableName})]; - 4
import org.apache.ibatis.annotations.Mapper; - 5
- 6
@Mapper - 7
public interface [(${table.javaTableName})]Mapper extends BaseMapper<[(${table.javaTableName})]> { - 8
- 9
}
2创建表实体
创建实体SqlTable.java
- 1
package life.wqg.common.utils.code.entity; - 2
- 3
import java.util.List; - 4
- 5
public class SqlTable { - 6
/** 表名 */ - 7
private String tableName; - 8
- 9
- 10
/** 对应的java实体类的名称 */ - 11
private String javaTableName; - 12
- 13
/** 表里面的字段名 */ - 14
private List<SqlField> fieldList; - 15
- 16
/** 表里面的主键 */ - 17
private SqlField primaryKey; - 18
- 19
//省略getter setter - 20
}
创建实体SqlField.java
- 1
package life.wqg.common.utils.code.entity; - 2
- 3
public class SqlField { - 4
/**字段名称*/ - 5
private String name; - 6
- 7
/**驼蜂式命名*/ - 8
private String camelName; - 9
- 10
/**首字母大写的驼蜂式命名*/ - 11
private String firstUpCamelName; - 12
- 13
/**字段类型*/ - 14
private String type; - 15
- 16
- 17
/**java实体类的类型*/ - 18
private String javaType; - 19
- 20
- 21
/**备注*/ - 22
private String remark; - 23
- 24
- 25
/** 数据库类型 */ - 26
private String jdbcType; - 27
- 28
/** 是否为主键 */ - 29
private Boolean isPrimaryKey; - 30
- 31
- 32
//省略getter setter - 33
}
3创建生成工具类
- 1
package life.wqg.common.utils.code; - 2
- 3
import cn.hutool.core.io.IORuntimeException; - 4
import cn.hutool.core.io.file.FileReader; - 5
import cn.hutool.core.io.file.FileWriter; - 6
import cn.hutool.core.util.StrUtil; - 7
import life.wqg.common.utils.code.entity.SqlField; - 8
import life.wqg.common.utils.code.entity.SqlTable; - 9
import org.thymeleaf.TemplateEngine; - 10
import org.thymeleaf.context.Context; - 11
- 12
import javax.sql.DataSource; - 13
import java.io.File; - 14
import java.sql.Connection; - 15
import java.sql.DatabaseMetaData; - 16
import java.sql.ResultSet; - 17
import java.sql.SQLException; - 18
import java.util.*; - 19
import java.util.regex.Matcher; - 20
- 21
/** - 22
* 代码生成器 - 23
*/ - 24
public class CodeGenerator { - 25
- 26
- 27
private Map<String, String> property2JavaMap = new HashMap<>(); - 28
- 29
private Map<String, String> property2JdbcMap = new HashMap<>(); - 30
- 31
private DataSource dataSource = null; - 32
- 33
private TemplateEngine templateEngine = null; - 34
- 35
private String filePath = ""; - 36
- 37
private String pojoPackage = ""; - 38
- 39
private String daoPackage = ""; - 40
- 41
private String mapperPackage = ""; - 42
- 43
public CodeGenerator(DataSource dataSource, TemplateEngine templateEngine, String pojoPackage, String daoPackage, String mapperPackage) { - 44
this.dataSource = dataSource; - 45
this.templateEngine = templateEngine; - 46
this.filePath = System.getProperty("user.dir"); - 47
this.pojoPackage = pojoPackage; - 48
this.daoPackage = daoPackage; - 49
this.mapperPackage = mapperPackage; - 50
property2JavaMap.put("BIGINT UNSIGNED", "Long"); - 51
property2JavaMap.put("DATETIME", "Date"); - 52
property2JavaMap.put("TIMESTAMP", "Date"); - 53
property2JavaMap.put("VARCHAR", "String"); - 54
property2JavaMap.put("DECIMAL", "BigDecimal"); - 55
property2JavaMap.put("BIGINT", "Long"); - 56
property2JavaMap.put("TEXT", "String"); - 57
property2JavaMap.put("TINYINT", "Integer"); - 58
property2JavaMap.put("INT", "Integer"); - 59
property2JavaMap.put("BIT", "Integer"); - 60
property2JavaMap.put("CHAR", "String"); - 61
- 62
property2JdbcMap.put("BIGINT UNSIGNED", "BIGINT"); - 63
property2JdbcMap.put("DATETIME", "TIMESTAMP"); - 64
property2JdbcMap.put("TIMESTAMP", "TIMESTAMP"); - 65
property2JdbcMap.put("VARCHAR", "VARCHAR"); - 66
property2JdbcMap.put("DECIMAL", "DECIMAL"); - 67
property2JdbcMap.put("BIGINT", "BIGINT"); - 68
property2JdbcMap.put("TEXT", "VARCHAR"); - 69
property2JdbcMap.put("TINYINT", "TINYINT"); - 70
property2JdbcMap.put("INT", "INTEGER"); - 71
property2JdbcMap.put("BIT", "BIT"); - 72
property2JdbcMap.put("CHAR", "CHAR"); - 73
} - 74
- 75
public SqlTable getTableInfo(String dbName, String tableName) { - 76
- 77
- 78
SqlTable table = new SqlTable(); - 79
//把tableName转成驼峰式,并且首子母改成大写 - 80
String javaTableName = StrUtil.upperFirst(StrUtil.toCamelCase(tableName)); - 81
table.setTableName(tableName); - 82
table.setJavaTableName(javaTableName); - 83
Connection conn = null; - 84
try { - 85
conn = dataSource.getConnection(); - 86
- 87
DatabaseMetaData metaData = conn.getMetaData(); - 88
ResultSet resultSet = metaData.getColumns(null, null, tableName, "%"); - 89
ResultSet pkRSet = metaData.getPrimaryKeys(null, null, tableName); - 90
Set<String> pkSet = new HashSet<>(); - 91
while (pkRSet.next()) { - 92
pkSet.add(pkRSet.getString("COLUMN_NAME")); - 93
} - 94
String columnName; - 95
String columnType; - 96
- 97
- 98
List<SqlField> fieldList = new ArrayList<>(); - 99
while (resultSet.next()) { - 100
if (!resultSet.getString("TABLE_CAT").equals(dbName)) { - 101
continue; - 102
} - 103
SqlField field = new SqlField(); - 104
columnName = resultSet.getString("COLUMN_NAME"); - 105
columnType = resultSet.getString("TYPE_NAME"); - 106
String remark = resultSet.getString("REMARKS"); - 107
field.setName(columnName); - 108
field.setIsPrimaryKey(pkSet.contains(columnName)); - 109
field.setRemark(remark); - 110
field.setType(columnType); - 111
field.setCamelName(StrUtil.toCamelCase(columnName)); - 112
field.setFirstUpCamelName(StrUtil.upperFirst(field.getCamelName())); - 113
field.setJavaType(property2JavaMap.get(columnType)); - 114
field.setJdbcType(property2JdbcMap.get(columnType)); - 115
fieldList.add(field); - 116
if (pkSet.contains(columnName)){ - 117
//主键 - 118
table.setPrimaryKey(field); - 119
} - 120
} - 121
table.setFieldList(fieldList); - 122
} catch (SQLException ex) { - 123
ex.printStackTrace(); - 124
} - 125
return table; - 126
} - 127
- 128
- 129
/** - 130
* 开始生成代码 - 131
*/ - 132
public void startGenerate(String dbName, String tableName, boolean isGeneratePojo, boolean isGenerateDao, boolean isGenerateXml,boolean isOverwriteDao) { - 133
SqlTable tableInfo = this.getTableInfo(dbName, tableName); - 134
Context ctx = new Context(Locale.CHINA); - 135
if (isGeneratePojo) { - 136
generatePojo(ctx, tableInfo); - 137
} - 138
if (isGenerateDao) { - 139
generateDao(ctx, tableInfo,isOverwriteDao); - 140
} - 141
if (isGenerateXml) { - 142
generateXml(ctx,tableInfo); - 143
} - 144
} - 145
- 146
/** - 147
* 生成xml - 148
* @param ctx - 149
* @param tableInfo - 150
*/ - 151
private void generateXml(Context ctx, SqlTable tableInfo) { - 152
Map<String, Object> map = new HashMap<>(); - 153
map.put("table", tableInfo); - 154
map.put("pojoPackage", pojoPackage); - 155
map.put("daoPackage", daoPackage); - 156
ctx.setVariables(map); - 157
String process = templateEngine.process("code/mapperTemplate.txt", ctx); - 158
FileWriter writer = new FileWriter(filePath + "\\src\\main\\resources\\" + mapperPackage.replaceAll("\\.", Matcher.quoteReplacement(File.separator)) + "\\" + tableInfo.getJavaTableName() + "Mapper.xml"); - 159
writer.write(process); - 160
} - 161
- 162
/** - 163
* 生成Dao - 164
* - 165
* @param ctx - 166
* @param tableInfo - 167
*/ - 168
private void generateDao(Context ctx, SqlTable tableInfo,boolean isOverwriteDao) { - 169
if (!isOverwriteDao){ - 170
try { - 171
FileReader fileReader = new FileReader(filePath + "\\src\\main\\java\\" + daoPackage.replaceAll("\\.", Matcher.quoteReplacement(File.separator)) + "\\" + tableInfo.getJavaTableName() + "Mapper.java"); - 172
String result = fileReader.readString(); - 173
if (!StrUtil.isEmpty(result)){ - 174
return; - 175
} - 176
}catch (IORuntimeException exception){ - 177
System.out.println("文件不存在,已生成"); - 178
} - 179
- 180
} - 181
Map<String, Object> map = new HashMap<>(); - 182
map.put("table", tableInfo); - 183
map.put("pojoPackage", pojoPackage); - 184
map.put("daoPackage", daoPackage); - 185
ctx.setVariables(map); - 186
String process = templateEngine.process("code/daoTemplate.txt", ctx); - 187
FileWriter writer = null; - 188
writer = new FileWriter(filePath + "\\src\\main\\java\\" + daoPackage.replaceAll("\\.", Matcher.quoteReplacement(File.separator)) + "\\" + tableInfo.getJavaTableName() + "Mapper.java"); - 189
writer.write(process); - 190
} - 191
- 192
/** - 193
* 生成pojo - 194
* - 195
* @param ctx - 196
*/ - 197
private void generatePojo(Context ctx, SqlTable tableInfo) { - 198
Map<String, Object> map = new HashMap<>(); - 199
map.put("table", tableInfo); - 200
map.put("pojoPackage", pojoPackage); - 201
ctx.setVariables(map); - 202
String process = templateEngine.process("code/pojoTemplate.txt", ctx); - 203
FileWriter writer = null; - 204
writer = new FileWriter(filePath + "\\src\\main\\java\\" + pojoPackage.replaceAll("\\.", Matcher.quoteReplacement(File.separator)) + "\\" + tableInfo.getJavaTableName() + ".java"); - 205
writer.write(process); - 206
} - 207
- 208
- 209
public static void main(String[] args) { - 210
System.out.printf(System.getProperty("user.dir")); - 211
/* FileWriter writer = new FileWriter("test.properties"); - 212
writer.write("test");*/ - 213
} - 214
- 215
}
4调用生成
- 1
package life.wqg; - 2
- 3
import com.upyun.UpException; - 4
import life.wqg.common.utils.code.CodeGenerator; - 5
import org.springframework.beans.factory.annotation.Autowired; - 6
import org.springframework.boot.test.context.SpringBootTest; - 7
import org.thymeleaf.TemplateEngine; - 8
import org.thymeleaf.templatemode.TemplateMode; - 9
import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver; - 10
- 11
import javax.sql.DataSource; - 12
import java.io.IOException; - 13
import java.sql.SQLException; - 14
import java.util.Collections; - 15
- 16
@SpringBootTest - 17
public class GeneratorTest { - 18
@Autowired - 19
private DataSource dataSource; - 20
- 21
@Autowired - 22
private TemplateEngine templateEngine; - 23
- 24
@org.junit.jupiter.api.Test - 25
public void generator() throws SQLException, UpException, IOException { - 26
String dbName = "test"; - 27
CodeGenerator codeGenerator = new CodeGenerator(dataSource,templateEngine,"life.wqg.pojo","life.wqg.dao","mapper"); - 28
codeGenerator.startGenerate(dbName,"user_info",true,true,true,false); - 29
- 30
} - 31
}
还没有评论。