01、需求
基于数据表结构生成java实体类。可以指定数据表,可以生成注解,自动引入包,生成字段注释。
02、思路
整个过程可以拆解成以下几个步骤:
- 连接数据库
- 获取数据表结构信息
- 解析数据表结构信息,包括字段名、表名下划线转驼峰, mysql数据类型转java包装类等。
- 根据解析后的数据使用类模板生成代码
- 获取工程路径,根据输入的包路径获取实体类生成位置
- 使用文件输出流输出.java文件
03、代码实现
数据列信息实体类
@Data
public class ColumnInfo {
/**
* 主键标识
*/
private Integer Key;
/**
* 列名称
*/
private String name;
/**
* 数据类型
*/
private Integer dataType;
/**
* 数据类型名称
*/
private String dataTypeName;
/**
* 自增标识
*/
private Integer AutoIncrement;
/**
* 精度
*/
private Integer precision;
/**
* 是否为空
*/
private Integer isNull;
/**
* 小数位数
*/
private Integer scale;
/**
* 默认值
*/
private String defaultValue;
/**
* 注释
*/
private String comment;
}
复制代码
数据库连接及表结构信息获取
public class DataBaseScan {
private String dataBaseUrl;
private Integer port;
private String dataBaseName;
private String userName;
private String password;
private static final long serialVersionUID = 1L;
Connection conn = null;
Statement st = null;
/**
* @param dataBaseUrl
* @param port
* @param userName
* @param password
*/
public DataBaseScan(String dataBaseUrl, Integer port, String dataBaseName, String userName, String password) {
this.dataBaseUrl = dataBaseUrl;
this.port = port;
this.userName = userName;
this.password = password;
this.dataBaseName = dataBaseName;
}
//获取conn
public void init() {
try {
if (StringUtils.isBlank(dataBaseUrl) || StringUtils.isBlank(dataBaseName) || StringUtils.isBlank(userName) || port == null) {
throw new IllegalArgumentException("数据库连接信息缺失!");
} else {
Class.forName("com.mysql.jdbc.Driver").newInstance();
conn = java.sql.DriverManager.getConnection("jdbc:mysql://"+ dataBaseUrl +":" + port + "/" + dataBaseName, userName, password);
}
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
}
public List<ColumnInfo> getTableInfo(String tableName) throws SQLException {
init();
DatabaseMetaData dbmd = conn.getMetaData();
ResultSet pkrs = dbmd.getPrimaryKeys(null, null, tableName);
String pkrsName = "";
while(pkrs.next()){
pkrsName = pkrs.getString("COLUMN_NAME");
}
ResultSet rs = dbmd.getColumns(null, null, tableName.toUpperCase(), null);
/**判断字段是否自增*/
String sql = "select * from " + tableName + " where 1=2";
ResultSet rst = conn.prepareStatement(sql).executeQuery();
ResultSetMetaData rsmd = rst.getMetaData();
int i=1;
List<ColumnInfo> table = new ArrayList<>();
while(rs.next()){
//列名称
String columnName = rs.getString("COLUMN_NAME");
//数据类型
int dataType = rs.getInt("DATA_TYPE");
//数据类型名称
String dataTypeName = rs.getString("TYPE_NAME");
//精度,列的大小
int precision = rs.getInt("COLUMN_SIZE");
//小数位数
int scale = rs.getInt("DECIMAL_DIGITS");
//是否为空
int isNull = rs.getInt("NULLABLE");
//字段默认值
String defaultValue = rs.getString("COLUMN_DEF");
//注释
String comennt = rs.getString("REMARKS");
//是否自增
boolean isAutoIncrement = false;
try {
isAutoIncrement = rsmd.isAutoIncrement(i);
} catch (SQLException e) {
break;
}
ColumnInfo col = new ColumnInfo();
col.setComment(comennt);
col.setName(columnName);
col.setDataType(dataType);
col.setDataTypeName(dataTypeName);
col.setPrecision(precision);
col.setScale(scale);
col.setIsNull(isNull);
col.setDefaultValue(defaultValue);
if (isAutoIncrement) {
col.setAutoIncrement(1);
} else {
col.setAutoIncrement(0);
}
if (columnName.equals(pkrsName)) {
col.setKey(1);
} else {
col.setKey(0);
}
table.add(col);
i++;
}
rs.close();
return table;
}
}
复制代码
mysql数据类型转java包装类
public class SqlTypeEnum {
public static String transToJavaType(String mysqlType) {
if (mysqlType == null) {
return "null";
}
switch (mysqlType) {
case "VARCHAR":
case "CHAR" :
case "TEXT" :
return "String";
case "BLOB" :
return "byte[]";
case "INTEGER" :
case "BIGINT" :
case "ID" :
return "Long";
case "TINYINT" :
case "SMALLINT" :
case "MEDIUMINT" :
case "BOOLEAN" :
case "INT" :
return "Integer";
case "BIT" :
return "Boolean";
case "FLOAT" :
return "Float";
case "DOUBLE" :
return "Double";
case "DECIMAL" :
return "BigDecimal";
case "DATE" :
case "YEAR" :
return "Date";
case "DATETIME" :
case "TIMESTAMP" :
return "Timestamp";
default :
return "null";
}
}
}
复制代码
数据表结构信息解析及实体类生成
public class PojoGenerator {
private DataBaseScan dataBaseScan = null;
/**
* 实体类生成构造方法
* @param dataBaseUrl 数据库连接地址
* @param port 端口
* @param dataBaseName 数据库名
* @param userName 用户名
* @param password 密码
*/
public PojoGenerator(String dataBaseUrl,Integer port, String dataBaseName, String userName, String password) {
dataBaseScan = new DataBaseScan(dataBaseUrl, port, dataBaseName, userName, password);
}
/**
* 实体类生成
* @param packagePath 实体类包路径
* @param tableName 表明
* @param ORMType orm框架类型 【可选:jpa,mybatis等】
*/
public void generate(String packagePath, String tableName, String ORMType) {
try {
List<ColumnInfo> tableInfo = dataBaseScan.getTableInfo(tableName);
if (CollectionUtils.isEmpty(tableInfo)) {
System.out.println("获取数据表信息失败!");
return;
}
String head = headGenerator(packagePath, tableName, ORMType);
String content = contentGenerator(tableInfo,ORMType);
String pojo = head + content + "}";
String folderPath = System.getProperty("user.dir") + "\src\main\java\" + packagePath.replace(".","\") + "\";
String className = transToHump(tableName, true) + ".java";
//文件流输出
File file = new File(folderPath + className);
try (FileOutputStream fileOutputStream = new FileOutputStream(file)) {
fileOutputStream.write(pojo.getBytes(StandardCharsets.UTF_8));
fileOutputStream.flush();
} catch (IOException ie) {
ie.printStackTrace();
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 生成头
* @param ORMType
* @return
*/
private String headGenerator(String packagePath, String tableName, String ORMType) {
String pack = "package " + packagePath +";\n";
String className = "public class " + transToHump(tableName,true) + " {\n";
String dependency;
String annotion;
if (ORMType.equals("jpa")) {
dependency = "import javax.persistence.Entity;\n" +
"import javax.persistence.GeneratedValue;\n" +
"import javax.persistence.GenerationType;\n" +
"import javax.persistence.Id;\n" +
"import java.util.Date;\n" +
"import java.math.BigDecimal;\n" +
"import lombok.Data;\n" +
"import java.sql.Timestamp; \n";
annotion = "@Data\n@Entity\n";
} else {
dependency = "import java.util.Date;\n" +
"import java.math.BigDecimal;\n" +
"import lombok.Data;\n" +
"import java.sql.Timestamp; \n";
annotion = "@Data\n";
}
return pack + dependency + "\n\n" + annotion + className;
}
private String contentGenerator(List<ColumnInfo> tableInfo, String ORMType) {
String modifier = "private ";
StringBuilder content = new StringBuilder();
for (ColumnInfo columnInfo : tableInfo) {
String annotion = "";
//字段名
String fieldName = transToHump(columnInfo.getName(),false);
//注释
String comment = "";
String fieldType = SqlTypeEnum.transToJavaType(columnInfo.getDataTypeName()) + gap(1);
if (!StringUtils.isBlank(columnInfo.getComment())) {
comment = "/**\n"
+ " * " + columnInfo.getComment() + "\n"
+ " */\n";
}
//主键注解
if (columnInfo.getKey() == 1 && ORMType.equals("jpa")) {
if (columnInfo.getAutoIncrement() == 1) {
annotion = "@Id\n" +
gap(4) + "@GeneratedValue(strategy= GenerationType.IDENTITY)\n";
} else {
annotion = "@Id\n";
}
}
if (!StringUtils.isBlank(comment) || !StringUtils.isBlank(annotion)) {
content.append(gap(4) + comment + annotion + gap(4) + modifier + fieldType + fieldName + ";\n");
} else {
content.append(gap(4) + comment + annotion + modifier + fieldType + fieldName + ";\n");
}
}
return content.toString();
}
/**
* 生成缩进
* @param num
* @return
*/
private String gap(int num) {
StringBuilder gap = new StringBuilder();
for (int i=0; i<num; i++) {
gap.append(" ");
}
return gap.toString();
}
/**
* 字段名转换成驼峰
* @param field
* @param isTableName 是否首字母大写
* @return
*/
private String transToHump(String field, boolean isTableName){
if (field == null){
return "";
}
StringBuilder s = new StringBuilder();
boolean flag = false;
for (int i=0; i<field.length(); i++){
if (field.charAt(i) == '_'){
flag = true;
continue;
}
if (flag){
flag = false;
//转换成大写
s.append(String.valueOf(field.charAt(i)).toUpperCase());
continue;
}
if (i == 0 && isTableName){
//首字母大写
s.append(String.valueOf(field.charAt(i)).toUpperCase());
} else {
s.append(field.charAt(i));
}
}
return s.toString();
}
复制代码
测试
@Test
void test() throws ServletException, IOException, SQLException {
//参数:数据库地址,端口,数据库名,用户名,密码
PojoGenerator pojoGenerator = new PojoGenerator("127.0.0.1",3306,"test","root","");
//参数:包路径,表名,orm框架类型
pojoGenerator.generate("com.feng.pojo", "news", "jpa");
}
复制代码
总结
由于作者使用jpa比较多,所以可选的orm框架只有jpa,其他的orm框架需要生成特殊注解的则需要自己手动输入。在生成包引入时,为了方便,我直接把常用的实体类包全部引入。由于懒得再写get、set方法的生成,生成器需要配合lombok插件使用。 总之还是不够智能。不过也勉强能满足使用。聊胜于无吧。