根据Navicate的数据库定义语句DDL生成实体类和Mybatis增删改查代码输出到控制台。
代码如下:
import lombok.Getter;
import lombok.Setter;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
/**
* 根据数据库定义语句DDL生成实体类和Mybatis增删改查代码
*
* @author java_t_t
* @since 2023-04-22
*/
public class UseDDLGenerateCode {
/**
* 换行符
*/
private static final String LINE_SEPARATOR = "\n";
/**
* java默认import为:import java.lang.*
*/
private static final String DEFAULT_IMPORT = "java.lang.";
/**
* 空字符串,主要用于生成实体类代码时调整格式用
*/
private static final String SPACE = "";
/**
* 年月日格式化器
*/
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
/**
* jdbc类型与java数据类型的映射
*/
private static final Map<String, String> JDBC_JAVA_TYPE_MAP = new HashMap<>();
/**
* MySQL数据类型与jdbc数据类型的映射
*/
private static final Map<String, String> DB_JDBC_TYPE_MAP = new HashMap<>();
static {
// ====================jdbc与java type映射====================
// 数值类型
JDBC_JAVA_TYPE_MAP.put("TINYINT", "java.lang.Integer");
JDBC_JAVA_TYPE_MAP.put("SMALLINT", "java.lang.Integer");
JDBC_JAVA_TYPE_MAP.put("MEDIUMINT", "java.lang.Integer");
JDBC_JAVA_TYPE_MAP.put("INT", "java.lang.Integer");
JDBC_JAVA_TYPE_MAP.put("INTEGER", "java.lang.Integer");
JDBC_JAVA_TYPE_MAP.put("BIGINT", "java.lang.Long");
JDBC_JAVA_TYPE_MAP.put("FLOAT", "java.lang.Float");
JDBC_JAVA_TYPE_MAP.put("DOUBLE", "java.lang.Double");
JDBC_JAVA_TYPE_MAP.put("DECIMAL", "java.math.BigDecimal");
// 字符串类型
JDBC_JAVA_TYPE_MAP.put("CHAR", "java.lang.String");
JDBC_JAVA_TYPE_MAP.put("VARCHAR", "java.lang.String");
JDBC_JAVA_TYPE_MAP.put("TINYTEXT", "java.lang.String");
JDBC_JAVA_TYPE_MAP.put("TEXT", "java.lang.String");
JDBC_JAVA_TYPE_MAP.put("MEDIUMTEXT", "java.lang.String");
JDBC_JAVA_TYPE_MAP.put("LONGTEXT", "java.lang.String");
JDBC_JAVA_TYPE_MAP.put("ENUM", "java.lang.String");
JDBC_JAVA_TYPE_MAP.put("SET", "java.lang.String");
// 时间类型
JDBC_JAVA_TYPE_MAP.put("YEAR", "java.time.Year");
JDBC_JAVA_TYPE_MAP.put("DATE", "java.time.LocalDate");
JDBC_JAVA_TYPE_MAP.put("TIME", "java.time.LocalTime");
JDBC_JAVA_TYPE_MAP.put("DATETIME", "java.time.LocalDateTime");
JDBC_JAVA_TYPE_MAP.put("TIMESTAMP", "java.time.LocalDateTime");
// 二进制类型(二进制不知道该使用什么类型去接收,下面代码注释掉,仅作为MySQL数据类型的参考)
/*JDBC_JAVA_TYPE_MAP.put("BIT", "java.lang.Byte[]");
JDBC_JAVA_TYPE_MAP.put("BINARY", "java.lang.Byte[]");
JDBC_JAVA_TYPE_MAP.put("VARBINARY", "java.lang.Byte[]");
JDBC_JAVA_TYPE_MAP.put("TINYBLOB", "java.lang.Byte[]");
JDBC_JAVA_TYPE_MAP.put("BLOB", "java.lang.Byte[]");
JDBC_JAVA_TYPE_MAP.put("MEDIUMBLOB", "java.lang.Byte[]");
JDBC_JAVA_TYPE_MAP.put("LONGBLOB", "java.lang.Byte[]");*/
// ====================数据库字段类型与jdbc type映射====================
// 数值类型
DB_JDBC_TYPE_MAP.put("tinyint", "TINYINT");
DB_JDBC_TYPE_MAP.put("smallint", "SMALLINT");
DB_JDBC_TYPE_MAP.put("mediumint", "INTEGER");
DB_JDBC_TYPE_MAP.put("int", "INTEGER");
DB_JDBC_TYPE_MAP.put("integer", "INTEGER");
DB_JDBC_TYPE_MAP.put("bigint", "BIGINT");
DB_JDBC_TYPE_MAP.put("float", "FLOAT");
DB_JDBC_TYPE_MAP.put("double", "DOUBLE");
DB_JDBC_TYPE_MAP.put("decimal", "DECIMAL");
// 字符串类型
DB_JDBC_TYPE_MAP.put("char", "CHAR");
DB_JDBC_TYPE_MAP.put("varchar", "VARCHAR");
DB_JDBC_TYPE_MAP.put("tinytext", "VARCHAR");
DB_JDBC_TYPE_MAP.put("text", "VARCHAR");
DB_JDBC_TYPE_MAP.put("mediumtext", "VARCHAR");
DB_JDBC_TYPE_MAP.put("longtext", "VARCHAR");
DB_JDBC_TYPE_MAP.put("enum", "VARCHAR");
DB_JDBC_TYPE_MAP.put("set", "VARCHAR");
// 时间类型
DB_JDBC_TYPE_MAP.put("year", "DATE");
DB_JDBC_TYPE_MAP.put("date", "DATE");
DB_JDBC_TYPE_MAP.put("time", "TIME");
DB_JDBC_TYPE_MAP.put("datetime", "TIMESTAMP");
DB_JDBC_TYPE_MAP.put("timestamp", "TIMESTAMP");
// 二进制类型
DB_JDBC_TYPE_MAP.put("bit", "BIT");
DB_JDBC_TYPE_MAP.put("binary", "BINARY");
DB_JDBC_TYPE_MAP.put("varbinary", "VARBINARY");
DB_JDBC_TYPE_MAP.put("tinyblob", "BLOB");
DB_JDBC_TYPE_MAP.put("blob", "BLOB");
DB_JDBC_TYPE_MAP.put("mediumblob", "BLOB");
DB_JDBC_TYPE_MAP.put("longblob", "BLOB");
}
/**
* 根据数据库定义语句DDL生成实体类和Mybatis增删改查代码
*
* @param ddl DDL语句(DDL从navicate复制出来,其它手写的格式不一定支持)
* @return 解析后的表信息
*/
public static void generateCode(String ddl) {
TableInfo tableInfo = parseDDL(ddl);
generateEntity(tableInfo);
generateInsert(tableInfo);
generateUpdate(tableInfo);
generateDelete(tableInfo);
generateSelect(tableInfo);
generateResultMap(tableInfo);
}
/**
* 生成Mybatis的resultMap代码
*
* @param tableInfo 表信息
*/
private static void generateResultMap(TableInfo tableInfo) {
List<String> codes = new ArrayList<>();
codes.add(String.format(" <resultMap id=\"%sResultMap\" type=\"xxx.%s\">",
tableInfo.getJavaTableName(), tableInfo.getJavaTableName()));
List<Property> properties = tableInfo.getProperties();
for (int index = 0; index < properties.size(); index++) {
Property property = properties.get(index);
codes.add(String.format(" <result column=\"%s\" property=\"%s\" jdbcType=\"%s\" javaType=\"%s\"/>",
property.getColumn(), property.getProperty(), property.getJdbcType(), property.getJavaType()));
}
codes.add(" </resultMap>");
printCode(codes, "ResultMap");
}
/**
* 生成Mybatis的select代码
*
* @param tableInfo 表信息
*/
private static void generateSelect(TableInfo tableInfo) {
List<String> codes = new ArrayList<>();
codes.add(String.format(" <select id=\"select\" resultType=\"xxx.%s\">", tableInfo.getJavaTableName()));
codes.add(" SELECT");
List<Property> properties = tableInfo.getProperties();
for (int index = 0; index < properties.size(); index++) {
Property property = properties.get(index);
if (index < properties.size() - 1) {
codes.add(String.format(" `%s` as `%s`,", property.getColumn(), property.getProperty()));
} else {
codes.add(String.format(" `%s` as `%s`", property.getColumn(), property.getProperty()));
}
}
codes.add(String.format(" FROM `%s`", tableInfo.getJdbcTableName()));
codes.add(" WHERE <!--todo add condition>-->");
codes.add(" </select>");
printCode(codes, "Select");
}
/**
* 生成Mybatis的delete代码
*
* @param tableInfo 表信息
*/
private static void generateDelete(TableInfo tableInfo) {
List<String> codes = new ArrayList<>();
codes.add(String.format(" <delete id=\"delete\" parameterType=\"xxx.%s\">", tableInfo.getJavaTableName()));
codes.add(String.format(" DELETE FROM `%s`", tableInfo.getJdbcTableName()));
codes.add(" WHERE <!--todo add condition>-->");
codes.add(" </delete>");
printCode(codes, "Delete");
}
/**
* 生成Mybatis的update代码
*
* @param tableInfo 表信息
*/
private static void generateUpdate(TableInfo tableInfo) {
List<String> codes = new ArrayList<>();
codes.add(String.format(" <update id=\"update\" parameterType=\"xxx.%s\">", tableInfo.getJavaTableName()));
codes.add(String.format(" UPDATE `%s` SET", tableInfo.getJdbcTableName()));
List<Property> properties = tableInfo.getProperties();
for (int index = 0; index < properties.size(); index++) {
Property property = properties.get(index);
if (index < properties.size() - 1) {
codes.add(String.format(" `%s` = #{item.%s},", property.getColumn(), property.getProperty()));
} else {
codes.add(String.format(" `%s` = #{item.%s}", property.getColumn(), property.getProperty()));
}
}
codes.add(" WHERE <!--todo add condition>-->");
codes.add(" </update>");
printCode(codes, "Update");
}
/**
* 生成Mybatis的insert代码
*
* @param tableInfo 表信息
*/
private static void generateInsert(TableInfo tableInfo) {
List<String> codes = new ArrayList<>();
codes.add(String.format(" <insert id=\"insert\" parameterType=\"xxx.%s\">", tableInfo.getJavaTableName()));
codes.add(String.format(" INSERT INTO `%s`(", tableInfo.getJdbcTableName()));
List<Property> properties = tableInfo.getProperties();
for (int index = 0; index < properties.size(); index++) {
Property property = properties.get(index);
if (index < properties.size() - 1) {
codes.add(String.format(" `%s`,", property.getColumn()));
} else {
codes.add(String.format(" `%s`", property.getColumn()));
}
}
codes.add(" ) VALUES");
codes.add(" <foreach collection=\"list\" open=\"(\" separator=\"),(\" close=\")\" item=\"item\">");
for (int index = 0; index < properties.size(); index++) {
Property property = properties.get(index);
if (index < properties.size() - 1) {
codes.add(String.format(" #{item.%s},", property.getProperty()));
} else {
codes.add(String.format(" #{item.%s}", property.getProperty()));
}
}
codes.add(" </foreach>");
codes.add(" </insert>");
printCode(codes, "Insert");
}
/**
* 生成java实体类代码
*
* @param tableInfo 表信息
*/
private static void generateEntity(TableInfo tableInfo) {
List<String> codes = new ArrayList<>();
codes.add("import lombok.Getter;");
codes.add("import lombok.Setter;");
codes.add(SPACE);
codes.add(String.format("/**" + LINE_SEPARATOR +
" * %s" + LINE_SEPARATOR +
" *" + LINE_SEPARATOR +
" * @author auto" + LINE_SEPARATOR +
" * @since %s" + LINE_SEPARATOR +
" */", tableInfo.getJdbcTableName(), FORMATTER.format(LocalDateTime.now())));
codes.add("@Setter");
codes.add("@Getter");
codes.add(String.format("public class %s {", tableInfo.getJavaTableName()));
for (Property property : tableInfo.getProperties()) {
String javaType = property.getJavaType();
if (!javaType.startsWith(DEFAULT_IMPORT)) {
codes.add(0, String.format("import %s;", javaType));
}
javaType = javaType.substring(javaType.lastIndexOf(".") + 1);
String comment = (property.getComment() == null || property.getComment().isEmpty())
? property.getColumn() : property.getComment();
String field = String.format(" /**" + LINE_SEPARATOR +
" * %s" + LINE_SEPARATOR +
" */" + LINE_SEPARATOR +
" private %s %s;", comment, javaType, property.getProperty());
codes.add(field);
codes.add(SPACE);
}
if (SPACE.equals(codes.get(codes.size() - 1))) {
codes.remove(codes.size() - 1);
}
codes.add("}");
printCode(codes, "Entity Code");
}
/**
* 打印生成的代码
*
* @param codes 代码
* @param type 生成代码类型
*/
private static void printCode(List<String> codes, String type) {
System.out.println("====================" + type + "====================");
System.out.println(String.join(LINE_SEPARATOR, codes));
}
/**
* 解析数据库定义语句DDL
*
* @param ddl DDL语句(DDL从navicate复制出来,其它手写的格式不一定支持)
* @return 解析后的表信息
*/
private static TableInfo parseDDL(String ddl) {
TableInfo tableInfo = new TableInfo();
List<Property> properties = new ArrayList<>();
String[] lines = ddl.split(LINE_SEPARATOR);
for (String line : lines) {
if (line == null || line.trim().isEmpty()) {
continue;
}
line = line.trim().toLowerCase(Locale.ROOT);
if (line.contains("create table")) {
setTableName(tableInfo, line);
} else {
Property property = parseProperty(line);
if (property == null) {
continue;
}
properties.add(property);
}
}
tableInfo.setProperties(properties);
return tableInfo;
}
/**
* 解析表字段(类属性)
*
* @param line 表字段
* @return 类属性(表字段)信息
*/
private static Property parseProperty(String line) {
if (!line.startsWith("`")) {
return null;
}
String[] split = line.split(" ");
Property property = new Property();
for (int index = 0; index < split.length; index++) {
String str = split[index];
if (index == 0) {
String column = str.substring(1, str.length() - 1);
property.setColumn(column);
property.setProperty(underLineToCamel(column));
continue;
}
if (index == 1) {
int endIndex = str.indexOf("(");
String dbType = endIndex < 0 ? str : str.substring(0, endIndex);
property.setDbType(dbType);
property.setJdbcType(DB_JDBC_TYPE_MAP.getOrDefault(dbType, "UNKNOW"));
property.setJavaType(JDBC_JAVA_TYPE_MAP.getOrDefault(property.getJdbcType(), "UNKNOW"));
continue;
}
if ("comment".equals(str) && ++index < split.length) {
String comment = split[index];
comment = comment.substring(1, comment.length() - 2).trim();
property.setComment(comment);
}
}
return property;
}
/**
* 设置表名信息,包括数据库表名和对应的实体类类名
*
* @param tableInfo 表信息
* @param line 建表语句的CREATE TABLE所在行
*/
private static void setTableName(TableInfo tableInfo, String line) {
int start = line.indexOf("`") + 1;
String jdbcTableName = line.substring(start, line.indexOf("`", start));
String camel = underLineToCamel(jdbcTableName);
String javaTableName = caseFirstLetterToUpper(camel) + "Entity";
tableInfo.setJdbcTableName(jdbcTableName);
tableInfo.setJavaTableName(javaTableName);
}
/**
* 下划线转小驼峰
*
* @param underLine 下划线风格的变量名
* @return 小驼峰风格的变量名
*/
private static String underLineToCamel(String underLine) {
StringBuilder camel = new StringBuilder();
String[] words = underLine.split("_");
for (int index = 0; index < words.length; index++) {
String word = words[index];
if (word.isEmpty()) {
continue;
}
camel.append(index == 0 ? word : caseFirstLetterToUpper(word));
}
return camel.toString();
}
/**
* 首字母转大写
*
* @param word 单词
* @return 首字母转大写的单词
*/
private static String caseFirstLetterToUpper(String word) {
if (word.isEmpty() || word.length() == 1) {
return word.toLowerCase(Locale.ROOT);
}
char letter = word.charAt(0);
char upperLetter = (char) (letter - ('a' - 'A'));
return upperLetter + word.substring(1);
}
/**
* 表信息
*/
@Getter
@Setter
static class TableInfo {
/**
* 数据库表名
*/
private String jdbcTableName;
/**
* 实体类类名
*/
private String javaTableName;
/**
* 类属性(表字段)集合
*/
private List<Property> properties;
}
/**
* 类属性(表字段)
*/
@Getter
@Setter
static class Property {
/**
* 表字段名
*/
private String column;
/**
* 类属性名
*/
private String property;
/**
* 数据库表字段类型
*/
private String dbType;
/**
* jdbc类型
*/
private String jdbcType;
/**
* 类属性数据类型
*/
private String javaType;
/**
* 说明信息
*/
private String comment;
}
public static void main(String[] args) {
String ddl = "CREATE TABLE `student_info` (\n" +
" `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',\n" +
" `name` varchar(255) NOT NULL COMMENT 'name''\"''',\n" +
" `sex` tinyint(1) NOT NULL COMMENT '性`别',\n" +
" `address` varchar(255) NOT NULL COMMENT '地址',\n" +
" `birthday` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '生日',\n" +
" `find_type` int(11) DEFAULT NULL COMMENT '测试下划线命名法',\n" +
" `picture` blob COMMENT '证件照',\n" +
" PRIMARY KEY (`id`)\n" +
") ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4;";
generateCode(ddl);
}
}