前言
在公司开发应用中,数据库表、字段的命名通常要与类、属性命名相对应,例如字段命名为user_id,对应的属性名为userId,这样驼峰式的命名。
开发一个模块去维护单表数据时,当确定好表结构时准备开发的时候,执行完建表sql,一开始编写实体类,这个过程相当无聊,由于有了编写这样的工具类的想法。
先看看常用的建表sql(PostgreSQL版本),如下
CREATE TABLE ts_user (
id BIGINT NOT NULL,
name VARCHAR(100) NOT NULL,
sex NUMERIC(1,0) NOT null,
remark VARCHAR(500),
create_by VARCHAR(50) NOT NULL,
create_time TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP,
update_by VARCHAR(50),
update_time TIMESTAMP WITHOUT TIME ZONE,
CONSTRAINT pk_ts_user PRIMARY KEY (id)
);
COMMENT ON TABLE ts_user IS '用户表';
COMMENT ON COLUMN ts_user.id IS '主键';
COMMENT ON COLUMN ts_user.name IS '用户名';
COMMENT ON COLUMN ts_user.sex IS '性别(1.男2.女)';
COMMENT ON COLUMN ts_user.remark IS '备注';
COMMENT ON COLUMN ts_user.create_by IS '创建者';
COMMENT ON COLUMN ts_user.create_time IS '创建时间';
COMMENT ON COLUMN ts_user.update_by IS '更新者';
COMMENT ON COLUMN ts_user.update_time IS '更新时间';
ts是项目数据库的前缀,一般每个项目对应一个前缀。
由以上sql脚本,去掉表前缀,根据驼峰式的命名,类名应该为User,对应的属性名为id、name、sex、remark、createBy、createTime、updateBy、updateTime,对应的get/set方法与注释(尽管写注释过程很郁闷)。
实现代码如下:
package demo;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Sql2Java {
/** sql脚本文件*/
private static String sqlFile = "C:/Users/user/Desktop/assign_dept.sql";
/** 本地路径 */
private static String localPath = "E:/mavenwork/timesheet/src/main/java/";
/** 项目通用路径 */
private static String projectPath = "cn/com/pc/timesheet/";
/** 模块目录 */
private static String myPath = projectPath + "workform/";
/** 数据库表的前缀 */
public static String table_prefix = "ts";
/** 作者*/
private static String author = "@author wangweihong";
/** 实体类的包名 */
private static String packageName = "package @packPath@;\r\n\r\n@date@\r\n\r\n/** \r\n * @sign@\r\n * " + author + "\r\n */\r\n";
/** 导入Date*/
private static String importDate = "import java.util.Date;";
/** 存文件名称 */
private static Map fileNameMap = new LinkedHashMap();
/** 存类注释 */
private static Map tipMap = new LinkedHashMap();
/** 存get/set方法 */
private static Map methodMap = new LinkedHashMap();
/** 存字段 */
private static Map> dataMap = new HashMap>();
/** pg字段类型的集合 */
private static List isFieldList = new ArrayList();
private static List longList = Arrays.asList("BIGINT", "INT8", "BIGSERIAL", "SERIAL8");
private static List intList = Arrays.asList("SMALLINT", "INT2", "NUMERIC", "SERIAL", "SERIAL4" );
private static List doubleList = Arrays.asList("DECIMAL", "REAL", "FLOAT4", "DOUBLE", "FLOAT8");
private static List stringList = Arrays.asList("CHARACTER", "VARCHAR", "CHAR", "TEXT");
private static List timeList = Arrays.asList("INTERVAL", "TIMESTAMP", "DATE", "TIME");
static {
isFieldList.addAll(longList);
isFieldList.addAll(intList);
isFieldList.addAll(doubleList);
isFieldList.addAll(stringList);
isFieldList.addAll(timeList);
}
/**
* 数据库类型转代码类型
* @param fieldType 字段类型
* @return String
*/
public static String getClassType(String fieldType) {
String type = fieldType;
int idx = type.indexOf("(");
if (idx >= 0) {
type = type.substring(0, idx);
}
if (longList.contains(type)) {
return "long";
} else if (intList.contains(type)) {
if ("NUMERIC".equals(type)) {
String[] arr = fieldType.replaceAll("NUMERIC", "").replace("(", "").replace(")", "").split(",");
if (Integer.parseInt(arr[1]) > 1) {
return "double";
} else if (Integer.parseInt(arr[0]) > 5) {
return "long";
}
}
return "int";
} else if (doubleList.contains(type)) {
return "double";
} else if (stringList.contains(type)) {
return "String";
} else if (timeList.contains(type)) {
return "Date";
}
return "";
}
/**
* 校验是否为数据库字段
* @param str 字段
* @return boolean
*/
public static boolean checkField(String str) {
if (str.indexOf("NUMERIC") >= 0 || str.indexOf("VARCHAR") >= 0) {
return true;
}
return isFieldList.contains(strToArr(str.toUpperCase())[1]);
}
/**
* 多个空格转为一个空格并将字符串转为数组
* @param str
* @return String[]
*/
public static String[] strToArr(String str) {
str = dealSpace(str);
String[] arr = str.split(" ");
if (arr.length < 2) {
return Arrays.copyOf(arr, 2);
}
return arr;
}
/**
* 处理空格(去掉首尾空格且将多个空格转为一个空格)
* @param str 字符串
* @return String
*/
public static String dealSpace(String str) {
Pattern p = Pattern.compile("\\s+");
Matcher m = p.matcher(str.trim());
return m.replaceAll(" ");
}
/**
* 创建javabean文件
* @param inFile sql脚本路径
* @param outPath 目标目录
* @throws Exception
*/
public static void createBean(String inFile, String outPath) throws Exception {
BufferedReader reader = getReader(inFile);
String sign = null; //每一次读一行的数据
String className = null; //类名
String packagePath = myPath.replaceAll("/", ".") + entity
packageName = packageName.replace("@packPath@", packagePath);
while ((sign = reader.readLine()) != null) {
int index = 0;
sign = dealSpace(sign);
String upperSign = sign.toUpperCase(); //数据转大写
if ((index = upperSign.indexOf("CREATE TABLE")) >= 0) { //保存类型
className = getClassName(sign.substring(index + "CREATE TABLE".length(), sign.length()).replaceAll(" ", "").replace("(","").replace(table_prefix, ""));
fileNameMap.put(className, outPath + className);
Map map = new LinkedHashMap();
map.put("classHead", packageName + "public class " + className + " {\r\n\r\n");
dataMap.put(className, map);
} else if (checkField(upperSign)) { //保存属性与get/set方法
String[] arr = strToArr(sign);
String fieldName = getClassName(arr[0]); //属性名
String classType = getClassType(arr[1]); //属性类型
Map fieldMap = dataMap.get(className);
String classHead = fieldMap.get("classHead");
if ("Date".equals(classType) && classHead.indexOf("@date@") >= 0) {
classHead = classHead.replace("@date@", importDate);
fieldMap.put("classHead", classHead);
}
String fieldVal = "\tprivate " + classType + " " + fieldName + ";\r\n\r\n";
fieldMap.put(fieldName, fieldVal);
String oldMethod = methodMap.get(className);
String method = "\tpublic " + classType + " get"+ getMethodName(fieldName) + "() {\r\n\t\treturn " + fieldName
+ ";\r\n\t}\r\n\r\n\tpublic void set" + getMethodName(fieldName) + "(" + classType + " " + fieldName+") {\r\n\t\tthis."
+ fieldName + " = " + fieldName+";\r\n\t}\r\n\r\n";
if (oldMethod == null) {
methodMap.put(className, method);
} else {
methodMap.put(className, oldMethod + method);
}
} else if ((index = upperSign.indexOf("COMMENT ON TABLE")) >= 0) { //保存类注释
int idx = sign.indexOf(" IS ");
sign = sign.substring(idx + 5, sign.length() - 2);
sign = sign.replace("表", "");
tipMap.put(className, sign);
sign = sign + "实体类";
Map fieldMap = dataMap.get(className);
String classHead = fieldMap.get("classHead");
classHead = classHead.replace("@sign@", sign);
fieldMap.put("classHead", classHead);
} else if ((index = upperSign.indexOf("COMMENT ON COLUMN")) >= 0) { //保存属性注释
int idx = sign.indexOf(" IS ");
String field = getClassName(sign.substring(sign.indexOf(".") + 1, idx));
String tip = sign.substring(idx + 5, sign.length() - 2);
Map fieldMap = dataMap.get(className);
String val = fieldMap.get(field);
if (val != null) {
fieldMap.put(field, "\t/** " + tip + " */\r\n" + val);
}
} else if ((index = upperSign.indexOf("CONSTRAINT")) >= 0 || (index = upperSign.indexOf(");")) >= 0) {
continue;
}
}
reader.close();
for (Entry> entryMap : dataMap.entrySet()) {
String key = entryMap.getKey();
String outFilePath = fileNameMap.get(key);
BufferedWriter writer = getWriter(outFilePath + ".java");
Map fieldMap = entryMap.getValue();
String classHead = fieldMap.get("classHead");
if (classHead.indexOf("@date@") >= 0) {
fieldMap.put("classHead", classHead.replace("@date@", ""));
}
for (Entry entry : fieldMap.entrySet()) {
writer.write(entry.getValue());
}
writer.write(methodMap.get(key));
writer.write("}");
writer.close();
}
System.out.println("beanOver");
}
/**
* 将数据库字段格式转为类名格式
* @param str
* @return
*/
public static String getClassName(String str) {
int firIdx = 0;
while ((firIdx = str.indexOf("_")) >= 0) {
String first = str.substring(firIdx + 1, firIdx + 2);
str = str.replace("_"+first, first.toUpperCase());
}
return str;
}
/**
* 字符串转为首字母大写
* @param str 字符串
* @return String
*/
public static String getMethodName(String str) {
str = getClassName(str);
String firstName = str.substring(0, 1);
if (firstName.equals(firstName.toLowerCase())) {
str = firstName.toUpperCase() + str.substring(1);
}
return str;
}
/**
* 字符串转为变量格式
* @param str 字符串
* @return String
*/
public static String getFieldName(String str) {
str = getClassName(str);
String firstName = str.substring(0, 1);
if (!firstName.equals(firstName.toLowerCase())) {
str = firstName.toLowerCase() + str.substring(1);
}
return str;
}
/**
* 获取缓冲输入流
* @param file 路径
* @return BufferedWriter
* @throws IOException
*/
public static BufferedReader getReader(String fileUrl) throws IOException {
String filePath = fileUrl.substring(0, fileUrl.lastIndexOf("/") + 1);
File file = new File(filePath);
if (!file.exists() && !file.mkdirs()) {
throw new Exception("创建目录失败");
}
return new BufferedReader(new FileReader(fileUrl));
}
/**
* 获取缓冲输出流
* @param file 路径
* @return BufferedWriter
* @throws IOException
*/
public static BufferedWriter getWriter(String file) throws IOException {
return new BufferedWriter(new FileWriter(file));
}
public static void main(String[] args) throws Exception {
createBean(sqlFile, localPath + myPath + "entity/");
}
}
修改的变量
sqlFile:指定目录的sql脚本文件
localPath:本地环境的存java代码的目录
projectPath:项目的包路径
table_prefix:数据库表的前缀(没有可以不填)
author:作者
注意事项:
1、sql脚本以ANSI格式编码(或是跟项目编码一致),否则程序会报错(可使用NotePad++转)
2、sql脚本现阶段只支持PostgreSQL(建表sql需要执行成功)
3、生成javabean文件如果喜欢放在项目目录上,可以一早配置好了路径,想做测试或担心类型重复会覆盖代码可在调用createBean函数的第二个参数上写指定路径即可
运行后效果如下:
package cn.pconline.autooa.approval.domain;
import java.util.Date;
/**
* 用户实体类
* @author wangweihong
*/
public class User {
/** 主键 */
private long id;
/** 用户名 */
private String name;
/** 性别(1.男2.女) */
private int sex;
/** 备注 */
private String remark;
/** 创建者 */
private String createBy;
/** 创建时间 */
private Date createTime;
/** 更新者 */
private String updateBy;
/** 更新时间 */
private Date updateTime;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getSex() {
return sex;
}
public void setSex(int sex) {
this.sex = sex;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
public String getCreateBy() {
return createBy;
}
public void setCreateBy(String createBy) {
this.createBy = createBy;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public String getUpdateBy() {
return updateBy;
}
public void setUpdateBy(String updateBy) {
this.updateBy = updateBy;
}
public Date getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Date updateTime) {
this.updateTime = updateTime;
}
}
由于笔者比较懒,没有对其他数据库做兼容,笔者是根据自身编写的sql风格去生成的,无法满足所有sql风格,请见谅;如发现代码有bug,欢迎来访;代码如果看不懂,欢迎留言。