根据Navicate的数据库定义语句DDL生成实体类、Service层、Controller层和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.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
/**
* 根据数据库定义语句DDL生成实体类和Mybatis增删改查代码
*
* @author java_t_t
* @since 2023-04-22
*/
public class UseDDLGenerateCode {
/**
* 换行符
*/
private static final String LINE_SEPARATOR = System.getProperty("line.separator");
/**
* java默认import为:import java.lang.*
*/
private static final String DEFAULT_IMPORT = "java.lang.";
/**
* String
*/
private static final String STRING_TYPE = "java.lang.String";
/**
* 空字符串,主要用于生成实体类代码时调整格式用
*/
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<>();
/**
* package路径
*/
private static String packagePath = "xxx";
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");
// 二进制类型(二进制对应是否正确待确定)
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);
generateXmlMapper(tableInfo);
generateMapper(tableInfo);
generateService(tableInfo);
generateServiceImpl(tableInfo);
generateController(tableInfo);
}
/**
* 生成MyBatis的XML文件
*
* @param tableInfo 表信息
*/
private static void generateXmlMapper(TableInfo tableInfo) {
List<String> xml = new ArrayList<>();
xml.add("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
xml.add("<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">");
xml.add(String.format("<mapper namespace=\"%s.mapper.%s\">", packagePath, tableInfo.getMapperName()));
xml.addAll(generateInsert(tableInfo));
xml.addAll(generateBatchInsert(tableInfo));
xml.addAll(generateUpdate(tableInfo));
xml.addAll(generateDelete(tableInfo));
xml.addAll(generateGet(tableInfo));
xml.addAll(generateCount(tableInfo));
xml.addAll(generateList(tableInfo));
xml.addAll(generateResultMap(tableInfo));
xml.add("</mapper>");
printCode(xml, tableInfo.getMapperName() + " XML");
}
/**
* 生成MyBatis的resultMap代码
*
* @param tableInfo 表信息
* @return
*/
private static List<String> generateResultMap(TableInfo tableInfo) {
List<String> codes = new ArrayList<>();
codes.add(String.format(" <resultMap id=\"%sResultMap\" type=\"%s.entity.%s\">",
caseFirstLetterToLower(tableInfo.getVariableNamePrefix()), packagePath, tableInfo.getEntityName()));
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>");
return codes;
}
/**
* 生成MyBatis的list代码
*
* @param tableInfo 表信息
* @return
*/
private static List<String> generateList(TableInfo tableInfo) {
List<String> codes = new ArrayList<>();
codes.add(String.format(" <select id=\"list\" resultType=\"%s.entity.%s\">", packagePath,
tableInfo.getEntityName()));
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>");
for (int index = 0; index < properties.size(); index++) {
Property property = properties.get(index);
codes.add(String.format(" <if test=\"param.%s != null\">", property.getProperty()));
if (STRING_TYPE.equals(property.getJavaType())) {
codes.add(String.format(" AND `%s` like CONCAT(\"%%\", #{param.%s}, \"%%\")",
property.getColumn(),
property.getProperty()));
} else {
codes.add(String.format(" AND `%s` = #{param.%s}", property.getColumn(), property.getProperty()));
}
codes.add(String.format(" </if>"));
}
codes.add(" </where>");
codes.add(" LIMIT #{pageStart}, #{pageSize}");
codes.add(" </select>");
codes.add(SPACE);
return codes;
}
/**
* 生成MyBatis的Count代码
*
* @param tableInfo 表信息
* @return
*/
private static List<String> generateCount(TableInfo tableInfo) {
List<String> codes = new ArrayList<>();
codes.add(" <select id=\"count\" resultType=\"java.lang.Long\">");
codes.add(String.format(" SELECT COUNT(*) FROM `%s`", tableInfo.getJdbcTableName()));
List<Property> properties = tableInfo.getProperties();
codes.add(" <where>");
for (int index = 0; index < properties.size(); index++) {
Property property = properties.get(index);
codes.add(String.format(" <if test=\"param.%s != null\">", property.getProperty()));
if (STRING_TYPE.equals(property.getJavaType())) {
codes.add(String.format(" AND `%s` like CONCAT(\"%%\", #{param.%s}, \"%%\")",
property.getColumn(),
property.getProperty()));
} else {
codes.add(String.format(" AND `%s` = #{param.%s}", property.getColumn(), property.getProperty()));
}
codes.add(String.format(" </if>"));
}
codes.add(" </where>");
codes.add(" </select>");
codes.add(SPACE);
return codes;
}
/**
* 生成MyBatis的get代码
*
* @param tableInfo 表信息
* @return
*/
private static List<String> generateGet(TableInfo tableInfo) {
List<String> codes = new ArrayList<>();
codes.add(String.format(" <select id=\"get\" resultType=\"%s.entity.%s\">", packagePath,
tableInfo.getEntityName()));
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>");
for (int index = 0; index < properties.size(); index++) {
Property property = properties.get(index);
codes.add(String.format(" <if test=\"param.%s != null\">", property.getProperty()));
if (STRING_TYPE.equals(property.getJavaType())) {
codes.add(String.format(" AND `%s` like CONCAT(\"%%\", #{param.%s}, \"%%\")",
property.getColumn(),
property.getProperty()));
} else {
codes.add(String.format(" AND `%s` = #{param.%s}", property.getColumn(), property.getProperty()));
}
codes.add(String.format(" </if>"));
}
codes.add(" </where>");
codes.add(" LIMIT 1");
codes.add(" </select>");
codes.add(SPACE);
return codes;
}
/**
* 生成MyBatis的delete代码
*
* @param tableInfo 表信息
* @return
*/
private static List<String> generateDelete(TableInfo tableInfo) {
List<String> codes = new ArrayList<>();
codes.add(String.format(" <delete id=\"delete\" parameterType=\"%s.entity.%s\">",
packagePath, tableInfo.getEntityName()));
codes.add(String.format(" DELETE FROM `%s`", tableInfo.getJdbcTableName()));
codes.add(" WHERE `id` = #{id} <!--todo modify condition>-->");
codes.add(" </delete>");
codes.add(SPACE);
return codes;
}
/**
* 生成MyBatis的update代码
*
* @param tableInfo 表信息
* @return
*/
private static List<String> generateUpdate(TableInfo tableInfo) {
List<String> codes = new ArrayList<>();
codes.add(String.format(" <update id=\"update\" parameterType=\"%s.entity.%s\">",
packagePath, tableInfo.getEntityName()));
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>");
codes.add(SPACE);
return codes;
}
/**
* 生成MyBatis的batchInsert代码
*
* @param tableInfo 表信息
* @return
*/
private static List<String> generateBatchInsert(TableInfo tableInfo) {
List<String> codes = new ArrayList<>();
codes.add(String.format(" <insert id=\"batchInsert\" parameterType=\"%s.entity.%s\">",
packagePath, tableInfo.getEntityName()));
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>");
codes.add(SPACE);
return codes;
}
/**
* 生成MyBatis的insert代码
*
* @param tableInfo 表信息
* @return
*/
private static List<String> generateInsert(TableInfo tableInfo) {
List<String> codes = new ArrayList<>();
codes.add(String.format(" <insert id=\"insert\" parameterType=\"%s.entity.%s\">",
packagePath, tableInfo.getEntityName()));
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 (");
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(" </insert>");
codes.add(SPACE);
return codes;
}
/**
* 生成java实体类代码
*
* @param tableInfo 表信息
*/
private static void generateEntity(TableInfo tableInfo) {
List<String> codes = new ArrayList<>();
codes.add(String.format("package %s.entity;", packagePath));
codes.add(SPACE);
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.getEntityName()));
Set<String> imported = new HashSet<>();
for (Property property : tableInfo.getProperties()) {
String javaType = property.getJavaType();
if (!javaType.startsWith(DEFAULT_IMPORT) && !imported.contains(javaType)) {
codes.add(2, String.format("import %s;", javaType));
imported.add(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 (!imported.isEmpty()) {
codes.add(imported.size() + 2, SPACE);
}
if (SPACE.equals(codes.get(codes.size() - 1))) {
codes.remove(codes.size() - 1);
}
codes.add("}");
printCode(codes, tableInfo.getEntityName());
}
/**
* 生成Controller代码
*
* @param tableInfo 表信息
*/
private static void generateController(TableInfo tableInfo) {
String serviceVar = caseFirstLetterToLower(tableInfo.getServiceName());
List<String> codes = new ArrayList<>();
codes.add(String.format("package %s.controller;", packagePath));
codes.add(SPACE);
codes.add(String.format("import %s.entity.%s;", packagePath, tableInfo.getEntityName()));
codes.add(String.format("import %s.service.%s;", packagePath, tableInfo.getServiceName()));
codes.add("import org.springframework.beans.factory.annotation.Autowired;");
codes.add("import org.springframework.web.bind.annotation.GetMapping;");
codes.add("import org.springframework.web.bind.annotation.PostMapping;");
codes.add("import org.springframework.web.bind.annotation.RequestBody;");
codes.add("import org.springframework.web.bind.annotation.RequestMapping;");
codes.add("import org.springframework.web.bind.annotation.RequestParam;");
codes.add("import org.springframework.web.bind.annotation.RestController;");
codes.add(SPACE);
codes.add("import java.util.List;");
codes.add(SPACE);
codes.add(String.format("/**" + LINE_SEPARATOR +
" * %s" + LINE_SEPARATOR +
" *" + LINE_SEPARATOR +
" * @author auto" + LINE_SEPARATOR +
" * @since %s" + LINE_SEPARATOR +
" */", tableInfo.getControllerName(), FORMATTER.format(LocalDateTime.now())));
codes.add("@RestController");
codes.add(String.format("@RequestMapping(\"/%s\")", tableInfo.getJdbcTableName()));
codes.add(String.format("public class %s {", tableInfo.getControllerName()));
codes.add(" @Autowired");
codes.add(String.format(" private %s %s;", tableInfo.getServiceName(), serviceVar));
codes.add(SPACE);
codes.add(" /**" + LINE_SEPARATOR +
" * 保存单条数据" + LINE_SEPARATOR +
" *" + LINE_SEPARATOR +
" * @param body 数据" + LINE_SEPARATOR +
" */");
codes.add(" @PostMapping(\"/save\")");
codes.add(String.format(" public void save(@RequestBody %s body) {", tableInfo.getEntityName()));
codes.add(String.format(" %s.save(body);", serviceVar));
codes.add(" }");
codes.add(SPACE);
codes.add(" /**" + LINE_SEPARATOR +
" * 批量保存数据" + LINE_SEPARATOR +
" *" + LINE_SEPARATOR +
" * @param list 多条数据" + LINE_SEPARATOR +
" */");
codes.add(" @PostMapping(\"/batchSave\")");
codes.add(String.format(" public void batchSave(@RequestBody List<%s> list) {", tableInfo.getEntityName()));
codes.add(String.format(" %s.batchSave(list);", serviceVar));
codes.add(" }");
codes.add(SPACE);
codes.add(" /**" + LINE_SEPARATOR +
" * 更新数据" + LINE_SEPARATOR +
" *" + LINE_SEPARATOR +
" * @param body 单条数据" + LINE_SEPARATOR +
" */");
codes.add(" @PostMapping(\"/update\")");
codes.add(String.format(" public void update(@RequestBody %s body) {", tableInfo.getEntityName()));
codes.add(String.format(" %s.update(body);", serviceVar));
codes.add(" }");
codes.add(SPACE);
codes.add(" /**" + LINE_SEPARATOR +
" * 根据id删除数据" + LINE_SEPARATOR +
" *" + LINE_SEPARATOR +
" * @param id id" + LINE_SEPARATOR +
" */");
codes.add(" @GetMapping(\"/delete\")");
codes.add(" public void delete(@RequestParam(value = \"id\") Integer id) {");
codes.add(String.format(" %s.delete(id);", serviceVar));
codes.add(" }");
codes.add(SPACE);
codes.add(" /**" + LINE_SEPARATOR +
" * 单条查询" + LINE_SEPARATOR +
" *" + LINE_SEPARATOR +
" * @param params 查询参数" + LINE_SEPARATOR +
" * @return 查询结果" + LINE_SEPARATOR +
" */");
codes.add(" @GetMapping(\"/get\")");
codes.add(String.format(" public %s get(%s params) {", tableInfo.getEntityName(), tableInfo.getEntityName()));
codes.add(String.format(" return %s.get(params);", serviceVar));
codes.add(" }");
codes.add(SPACE);
codes.add(" /**" + LINE_SEPARATOR +
" * 批量查询" + LINE_SEPARATOR +
" *" + LINE_SEPARATOR +
" * @param params 查询参数" + LINE_SEPARATOR +
" * @param pageIndex 页码" + LINE_SEPARATOR +
" * @param pageSize 页面大小" + LINE_SEPARATOR +
" * @return 查询结果" + LINE_SEPARATOR +
" */");
codes.add(" @GetMapping(\"/list\")");
codes.add(String.format(" public List<%s> list(%s params,", tableInfo.getEntityName(),
tableInfo.getEntityName()));
String formatSpace = generateFormatSpace(codes.get(codes.size() - 1));
codes.add(formatSpace.concat("@RequestParam(required = false, name = \"pageIndex\", defaultValue = \"1\") Integer pageIndex,"));
codes.add(formatSpace.concat("@RequestParam(required = false, name = \"pageSize\", defaultValue = \"10\") Integer pageSize) {"));
codes.add(String.format(" return %s.list(params, pageIndex, pageSize);", serviceVar));
codes.add(" }");
codes.add("}");
printCode(codes, tableInfo.getControllerName());
}
/**
* 生成对应的格式化空格
*
* @param methodCode 方法定义代码
* @return 格式化空格
*/
private static String generateFormatSpace(String methodCode) {
int indexOf = methodCode.indexOf("(");
if (indexOf == -1) {
return SPACE;
}
StringBuilder sb = new StringBuilder();
while (indexOf-- >= 0) {
sb.append(" ");
}
return sb.toString();
}
/**
* 生成ServiceImpl代码
*
* @param tableInfo 表信息
*/
private static void generateServiceImpl(TableInfo tableInfo) {
String mapperVar = caseFirstLetterToLower(tableInfo.getMapperName());
List<String> codes = new ArrayList<>();
codes.add(String.format("package %s.service.impl;", packagePath));
codes.add(SPACE);
codes.add(String.format("import %s.entity.%s;", packagePath, tableInfo.getEntityName()));
codes.add(String.format("import %s.mapper.%s;", packagePath, tableInfo.getMapperName()));
codes.add(String.format("import %s.service.%s;", packagePath, tableInfo.getServiceName()));
codes.add("import org.springframework.beans.factory.annotation.Autowired;");
codes.add("import org.springframework.stereotype.Service;");
codes.add(SPACE);
codes.add("import java.util.List;");
codes.add(SPACE);
codes.add(String.format("/**" + LINE_SEPARATOR +
" * %s" + LINE_SEPARATOR +
" *" + LINE_SEPARATOR +
" * @author auto" + LINE_SEPARATOR +
" * @since %s" + LINE_SEPARATOR +
" */", tableInfo.getServiceImplName(), FORMATTER.format(LocalDateTime.now())));
codes.add("@Service");
codes.add(String.format("public class %s implements %s {", tableInfo.getServiceImplName(), tableInfo.getServiceName()));
codes.add(" @Autowired");
codes.add(String.format(" private %s %s;", tableInfo.getMapperName(), mapperVar));
codes.add(SPACE);
codes.add(" @Override");
codes.add(String.format(" public void save(%s item) {" + LINE_SEPARATOR +
" %s.insert(item);" + LINE_SEPARATOR +
" }", tableInfo.getEntityName(), mapperVar));
codes.add(SPACE);
codes.add(" @Override");
codes.add(String.format(" public void batchSave(List<%s> list) {" + LINE_SEPARATOR +
" %s.batchInsert(list);" + LINE_SEPARATOR +
" }", tableInfo.getEntityName(), mapperVar));
codes.add(SPACE);
codes.add(" @Override");
codes.add(String.format(" public void update(%s item) {" + LINE_SEPARATOR +
" %s.update(item);" + LINE_SEPARATOR +
" }", tableInfo.getEntityName(), mapperVar));
codes.add(SPACE);
codes.add(" @Override");
codes.add(String.format(" public void delete(Integer id) {" + LINE_SEPARATOR +
" %s.delete(id);" + LINE_SEPARATOR +
" }", mapperVar));
codes.add(SPACE);
codes.add(" @Override");
codes.add(String.format(" public %s get(%s param) {" + LINE_SEPARATOR +
" return %s.get(param);" + LINE_SEPARATOR +
" }", tableInfo.getEntityName(), tableInfo.getEntityName(), mapperVar));
codes.add(SPACE);
codes.add(" @Override");
codes.add(String.format(" public List<%s> list(%s param, Integer pageIndex, Integer pageSize) {" + LINE_SEPARATOR +
" return %s.list(param, (pageIndex - 1) * pageSize, pageSize);" + LINE_SEPARATOR +
" }", tableInfo.getEntityName(), tableInfo.getEntityName(), mapperVar));
codes.add("}");
printCode(codes, tableInfo.getServiceImplName());
}
/**
* 生成Service代码
*
* @param tableInfo 表信息
*/
private static void generateService(TableInfo tableInfo) {
List<String> codes = new ArrayList<>();
codes.add(String.format("package %s.service;", packagePath));
codes.add(SPACE);
codes.add(String.format("import %s.entity.%s;", packagePath, tableInfo.getEntityName()));
codes.add(SPACE);
codes.add("import java.util.List;");
codes.add(SPACE);
codes.add(String.format("/**" + LINE_SEPARATOR +
" * %s" + LINE_SEPARATOR +
" *" + LINE_SEPARATOR +
" * @author auto" + LINE_SEPARATOR +
" * @since %s" + LINE_SEPARATOR +
" */", tableInfo.getServiceName(), FORMATTER.format(LocalDateTime.now())));
codes.add(String.format("public interface %s {", tableInfo.getServiceName()));
codes.add(" /**" + LINE_SEPARATOR +
" * 保存单条数据" + LINE_SEPARATOR +
" *" + LINE_SEPARATOR +
" * @param item 数据" + LINE_SEPARATOR +
" */");
codes.add(String.format(" void save(%s item);", tableInfo.getEntityName()));
codes.add(SPACE);
codes.add(" /**" + LINE_SEPARATOR +
" * 批量保存数据" + LINE_SEPARATOR +
" *" + LINE_SEPARATOR +
" * @param list 多条数据" + LINE_SEPARATOR +
" */");
codes.add(String.format(" void batchSave(List<%s> list);", tableInfo.getEntityName()));
codes.add(SPACE);
codes.add(" /**" + LINE_SEPARATOR +
" * 更新数据" + LINE_SEPARATOR +
" *" + LINE_SEPARATOR +
" * @param item 单条数据" + LINE_SEPARATOR +
" */");
codes.add(String.format(" void update(%s item);", tableInfo.getEntityName()));
codes.add(SPACE);
codes.add(" /**" + LINE_SEPARATOR +
" * 根据id删除数据" + LINE_SEPARATOR +
" *" + LINE_SEPARATOR +
" * @param id id" + LINE_SEPARATOR +
" */");
codes.add(String.format(" void delete(Integer id);"));
codes.add(SPACE);
codes.add(" /**" + LINE_SEPARATOR +
" * 单条查询" + LINE_SEPARATOR +
" *" + LINE_SEPARATOR +
" * @param param 查询参数" + LINE_SEPARATOR +
" * @return 查询结果" + LINE_SEPARATOR +
" */");
codes.add(String.format(" %s get(%s param);", tableInfo.getEntityName(), tableInfo.getEntityName()));
codes.add(SPACE);
codes.add(" /**" + LINE_SEPARATOR +
" * 批量查询" + LINE_SEPARATOR +
" *" + LINE_SEPARATOR +
" * @param param 查询参数" + LINE_SEPARATOR +
" * @param pageIndex 页码" + LINE_SEPARATOR +
" * @param pageSize 页面大小" + LINE_SEPARATOR +
" * @return 查询结果" + LINE_SEPARATOR +
" */");
codes.add(String.format(" List<%s> list(%s param, Integer pageIndex, Integer pageSize);",
tableInfo.getEntityName(), tableInfo.getEntityName()));
codes.add("}");
printCode(codes, tableInfo.getServiceName());
}
/**
* 生成Mapper代码
*
* @param tableInfo 表信息
*/
private static void generateMapper(TableInfo tableInfo) {
List<String> codes = new ArrayList<>();
codes.add(String.format("package %s.mapper;", packagePath));
codes.add(SPACE);
codes.add(String.format("import %s.entity.%s;", packagePath, tableInfo.getEntityName()));
codes.add("import org.apache.ibatis.annotations.Mapper;");
codes.add("import org.apache.ibatis.annotations.Param;");
codes.add(SPACE);
codes.add("import java.util.List;");
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("@Mapper");
codes.add(String.format("public interface %s {", tableInfo.getMapperName()));
codes.add(" /**" + LINE_SEPARATOR +
" * 保存单条数据" + LINE_SEPARATOR +
" *" + LINE_SEPARATOR +
" * @param item 数据" + LINE_SEPARATOR +
" */");
codes.add(String.format(" void insert(@Param(\"item\") %s item);", tableInfo.getEntityName()));
codes.add(SPACE);
codes.add(" /**" + LINE_SEPARATOR +
" * 批量保存数据" + LINE_SEPARATOR +
" *" + LINE_SEPARATOR +
" * @param list 多条数据" + LINE_SEPARATOR +
" */");
codes.add(String.format(" void batchInsert(@Param(\"list\") List<%s> list);", tableInfo.getEntityName()));
codes.add(SPACE);
codes.add(" /**" + LINE_SEPARATOR +
" * 更新数据" + LINE_SEPARATOR +
" *" + LINE_SEPARATOR +
" * @param item 单条数据" + LINE_SEPARATOR +
" */");
codes.add(String.format(" void update(@Param(\"item\") %s item);", tableInfo.getEntityName()));
codes.add(SPACE);
codes.add(" /**" + LINE_SEPARATOR +
" * 根据id删除数据" + LINE_SEPARATOR +
" *" + LINE_SEPARATOR +
" * @param id id" + LINE_SEPARATOR +
" */");
codes.add(String.format(" void delete(@Param(\"id\") Integer id);"));
codes.add(SPACE);
codes.add(" /**" + LINE_SEPARATOR +
" * 单条查询" + LINE_SEPARATOR +
" *" + LINE_SEPARATOR +
" * @param param 查询参数" + LINE_SEPARATOR +
" * @return 查询结果" + LINE_SEPARATOR +
" */");
codes.add(String.format(" %s get(@Param(\"param\") %s param);", tableInfo.getEntityName(), tableInfo.getEntityName()));
codes.add(SPACE);
codes.add(" /**" + LINE_SEPARATOR +
" * 查询记录总数" + LINE_SEPARATOR +
" *" + LINE_SEPARATOR +
" * @param param 查询参数" + LINE_SEPARATOR +
" * @return 查询结果" + LINE_SEPARATOR +
" */");
codes.add(String.format(" Long count(@Param(\"param\") %s param);", tableInfo.getEntityName()));
codes.add(SPACE);
codes.add(" /**" + LINE_SEPARATOR +
" * 批量查询" + LINE_SEPARATOR +
" *" + LINE_SEPARATOR +
" * @param param 查询参数" + LINE_SEPARATOR +
" * @param pageStart 当前页面起始索引" + LINE_SEPARATOR +
" * @param pageSize 页面大小" + LINE_SEPARATOR +
" * @return 查询结果" + LINE_SEPARATOR +
" */");
codes.add(String.format(" List<%s> list(@Param(\"param\") %s param, @Param(\"pageStart\") " +
"Integer pageStart, @Param(\"pageSize\") Integer pageSize);", tableInfo.getEntityName(), tableInfo.getEntityName()));
codes.add("}");
printCode(codes, tableInfo.getMapperName());
}
/**
* 打印生成的代码
*
* @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")) {
setClassName(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 setClassName(TableInfo tableInfo, String line) {
int start = line.indexOf("`") + 1;
String jdbcTableName = line.substring(start, line.indexOf("`", start));
String camel = underLineToCamel(jdbcTableName);
String classNamePrefix = caseFirstLetterToUpper(camel);
tableInfo.setJdbcTableName(jdbcTableName);
tableInfo.setVariableNamePrefix(classNamePrefix);
tableInfo.setEntityName(classNamePrefix + "Entity");
tableInfo.setMapperName(classNamePrefix + "Mapper");
tableInfo.setServiceName(classNamePrefix + "Service");
tableInfo.setServiceImplName(classNamePrefix + "ServiceImpl");
tableInfo.setControllerName(classNamePrefix + "Controller");
}
/**
* 下划线转小驼峰
*
* @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.toUpperCase(Locale.ROOT);
}
char letter = word.charAt(0);
if (letter <= 'Z') {
return word;
}
char upperLetter = (char) (letter - ('a' - 'A'));
return upperLetter + word.substring(1);
}
/**
* 首字母转小写
*
* @param word 单词
* @return 首字母转小写的单词
*/
private static String caseFirstLetterToLower(String word) {
if (word.isEmpty() || word.length() == 1) {
return word.toLowerCase(Locale.ROOT);
}
char letter = word.charAt(0);
if (letter >= 'a') {
return word;
}
char lowerLetter = (char) (letter + ('a' - 'A'));
return lowerLetter + word.substring(1);
}
/**
* 表信息
*/
@Getter
@Setter
static class TableInfo {
/**
* 数据库表名
*/
private String jdbcTableName;
/**
* 变量名前缀(表名转小驼峰)
*/
private String variableNamePrefix;
/**
* 实体类类名
*/
private String entityName;
/**
* mapper类类名
*/
private String mapperName;
/**
* service类类名
*/
private String serviceName;
/**
* service实现类类类名
*/
private String serviceImplName;
/**
* controller类类名
*/
private String controllerName;
/**
* 类属性(表字段)集合
*/
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 `person_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` varchar(255) COMMENT '证件照',\n" +
" PRIMARY KEY (`id`)\n" +
") ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4;";
packagePath = "com.example.study";
generateCode(ddl);
}
}