交接项目要给数据字典,项目又是中途接手,本来就没有,设计的又没做。搞个小工具,方便一下。
原理:通过DataSource 来获取数据库和表信息,比sql来得方便而且面向多数据源(起码测试过几种数据库都没有问题)。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.sql.DataSource;
import java.io.FileOutputStream;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
/**
* 数据字典生成器
*/
public class DataDictionaryCreateUtil {
private final static DataDictionaryCreateUtil instance = new DataDictionaryCreateUtil();
private static final Logger log = LoggerFactory.getLogger(DataDictionaryCreateUtil.class);
public DataDictionaryCreateUtil() {
}
public static DataDictionaryCreateUtil getInstance() {
return instance;
}
public static class DataDictionaryInfo {
private String tableName;
private String tableRemarks;
private String columnName;
private String columnRemarks;
private boolean nullAble;
private String dataTypeName;
private String tableSchemaName;
public String getTableSchemaName() {
return tableSchemaName;
}
public void setTableSchemaName(String tableSchemaName) {
this.tableSchemaName = tableSchemaName;
}
public String getTableName() {
return tableName;
}
public void setTableName(String tableName) {
this.tableName = tableName;
}
public String getTableRemarks() {
return tableRemarks;
}
public void setTableRemarks(String tableRemarks) {
this.tableRemarks = tableRemarks;
}
public String getColumnName() {
return columnName;
}
public void setColumnName(String columnName) {
this.columnName = columnName;
}
public String getColumnRemarks() {
return columnRemarks;
}
public void setColumnRemarks(String columnRemarks) {
this.columnRemarks = columnRemarks;
}
public boolean isNullAble() {
return nullAble;
}
public void setNullAble(boolean nullAble) {
this.nullAble = nullAble;
}
public String getDataTypeName() {
return dataTypeName;
}
public void setDataTypeName(String dataTypeName) {
this.dataTypeName = dataTypeName;
}
}
/**
* html模板body前缀
*/
String templateHtmlPrefix = "\n" +
"\n" +
"\n" +
" \n" +
"\n" +
"\n" +
"\n";
/**
* html模板body后缀
*/
String templateHtmlSuffix =
"\n" +
"";
/**
* html 内容表格模板前缀
*/
String templateHtmlContentPrefix ="\n字段名 是否必填 数据类型 注释 \n";
/**
* html 内容表格模板前缀
*/
String templateHtmlContentSuffix ="
\n";
/**
* html 内容表格模板
*/
String templateHtmlContent = "%s %s %s %s \n";
/**
* 获取数据库下面所有表
* @param dataSource
* @param database
* @return
* @throws Exception
*/
public List getDatatable(DataSource dataSource,String database) throws Exception {
Connection connection = dataSource.getConnection();
DatabaseMetaData metaData = connection.getMetaData();
ResultSet tables = metaData.getTables(null, database, "%", new String[]{"TABLE"});
List list = new ArrayList<>();
while (tables.next()) {
// 根据指定列名称获取数据
String TABLE_NAME = tables.getString("TABLE_NAME");
list.add(TABLE_NAME);
}
return list;
}
/**
* 保存表数据
* @param fileUrl
* @param dataSource
* @param database
* @param tables
* @throws Exception
*/
public void saveTables(String fileUrl, DataSource dataSource,String database, List tables) throws Exception {
Connection conn = dataSource.getConnection();
ResultSet rs = null;
StringBuffer sb = new StringBuffer();
try {
for (String table : tables) {
//表名备注
String tableNameRemark = null;
List list = new ArrayList<>();
/**
* 设置连接属性,使得可获取到列的REMARK(备注)
*/
DatabaseMetaData dbmd = conn.getMetaData();
rs = dbmd.getTables(null, database, table, null);
while (rs.next()) {
tableNameRemark = rs.getString("REMARKS"); //表类别(可能为空)
}
/**
* 获取可在指定类别中使用的表列的描述。
* 方法原型:ResultSet getColumns(String catalog,String schemaPattern,String tableNamePattern,String columnNamePattern)
* catalog - 表所在的类别名称;""表示获取没有类别的列,null表示获取所有类别的列。
* schema - 表所在的模式名称(oracle中对应于Tablespace);""表示获取没有模式的列,null标识获取所有模式的列; 可包含单字符通配符("_"),或多字符通配符("%");
* tableNamePattern - 表名称;可包含单字符通配符("_"),或多字符通配符("%");
* columnNamePattern - 列名称; ""表示获取列名为""的列(当然获取不到);null表示获取所有的列;可包含单字符通配符("_"),或多字符通配符("%");
*/
rs = dbmd.getColumns(null, database, table, null);
while (rs.next()) {
DataDictionaryInfo dataDictionaryInfo = new DataDictionaryInfo();
String tableCat = rs.getString("TABLE_CAT"); //表类别(可能为空)
String tableSchemaName = rs.getString("TABLE_SCHEM"); //表模式(可能为空),在oracle中获取的是命名空间,其它数据库未知
String tableName_ = rs.getString("TABLE_NAME"); //表名
String columnName = rs.getString("COLUMN_NAME"); //列名
int dataType = rs.getInt("DATA_TYPE"); //对应的java.sql.Types的SQL类型(列类型ID)
String dataTypeName = rs.getString("TYPE_NAME"); //java.sql.Types类型名称(列类型名称)
int columnSize = rs.getInt("COLUMN_SIZE"); //列大小
int decimalDigits = rs.getInt("DECIMAL_DIGITS"); //小数位数
int numPrecRadix = rs.getInt("NUM_PREC_RADIX"); //基数(通常是10或2) --未知
/**
* 0 (columnNoNulls) - 该列不允许为空
* 1 (columnNullable) - 该列允许为空
* 2 (columnNullableUnknown) - 不确定该列是否为空
*/
int nullAble = rs.getInt("NULLABLE"); //是否允许为null
String remarks = rs.getString("REMARKS"); //列描述
String columnDef = rs.getString("COLUMN_DEF"); //默认值
int charOctetLength = rs.getInt("CHAR_OCTET_LENGTH"); // 对于 char 类型,该长度是列中的最大字节数
int ordinalPosition = rs.getInt("ORDINAL_POSITION"); //表中列的索引(从1开始)
// String pkName = rs.getString("PK_NAME"); //主键名称
/**
* ISO规则用来确定某一列的是否可为空(等同于NULLABLE的值:[ 0:'YES'; 1:'NO'; 2:''; ])
* YES -- 该列可以有空值;
* NO -- 该列不能为空;
* 空字符串--- 不知道该列是否可为空
*/
String isNullAble = rs.getString("IS_NULLABLE");
/**
* 指示此列是否是自动递增
* YES -- 该列是自动递增的
* NO -- 该列不是自动递增
* 空字串--- 不能确定该列是否自动递增
*/
//String isAutoincrement = rs.getString("IS_AUTOINCREMENT"); //该参数测试报错
// log.info(GsonHelper.defaultGson().toJson(rs));
// System.out.println(tableCat + " - " + tableSchemaName + " - " + tableName_ + " - " + columnName +
// " - " + dataType + " - " + dataTypeName + " - " + columnSize + " - " + decimalDigits + " - "
// + numPrecRadix + " - " + nullAble + " - " + remarks + " - " + columnDef + " - " + charOctetLength
// + " - " + ordinalPosition + " - " + isNullAble );
//表名
String tableName = tableName_;
//SchemaName
String SchemaName = tableSchemaName;
//字段名 columnName;
//是否必填 nullAble
//数据类型 dataTypeName
dataDictionaryInfo.setTableSchemaName(tableSchemaName);
dataDictionaryInfo.setTableName(tableName);
dataDictionaryInfo.setColumnName(columnName);
if ("varchar".equals(dataTypeName)) {
dataDictionaryInfo.setDataTypeName(dataTypeName + "(" + columnSize + ")");
} else {
dataDictionaryInfo.setDataTypeName(dataTypeName);
}
dataDictionaryInfo.setNullAble(nullAble == 1);
dataDictionaryInfo.setTableRemarks(tableNameRemark);
dataDictionaryInfo.setColumnRemarks(remarks);
// log.info(GsonHelper.defaultGson().toJson(dataDictionaryInfo));
list.add(dataDictionaryInfo);
}
if(list.size()==0){
log.error("找不到行参数:"+table);
continue;
}
sb.append("表名:" + list.get(0).getTableName() + "(" + tableNameRemark + ")" + ",模式:" + list.get(0).getTableSchemaName() + "
").append("\n");
sb.append(templateHtmlContentPrefix);
for (DataDictionaryInfo dataDictionaryInfo : list) {
sb.append(String.format(templateHtmlContent, dataDictionaryInfo.getColumnName(), !dataDictionaryInfo.nullAble ? "是" : "否", dataDictionaryInfo.getDataTypeName(), dataDictionaryInfo.getColumnRemarks() == null ? "" : dataDictionaryInfo.getColumnRemarks())).append("\n");
}
sb.append(templateHtmlContentSuffix);
sb.append("
").append("\n");
}
} catch (SQLException ex) {
ex.printStackTrace();
} finally {
close(rs, conn);
}
String fileData = templateHtmlPrefix + sb.toString() + templateHtmlSuffix;
saveFile(fileUrl, fileData);
}
/**
* 保存文件
* @param fileUrl
* @param data
*/
private void saveFile(String fileUrl, String data) {
FileOutputStream fos = null;
try {
fos = new FileOutputStream(fileUrl + "/TempDataDictionary.html");
fos.write(data.getBytes());
fos.flush();
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
//关闭连接
public static void close(Object o) {
if (o == null) {
return;
}
if (o instanceof ResultSet) {
try {
((ResultSet) o).close();
} catch (SQLException e) {
e.printStackTrace();
}
} else if (o instanceof Statement) {
try {
((Statement) o).close();
} catch (SQLException e) {
e.printStackTrace();
}
} else if (o instanceof Connection) {
Connection c = (Connection) o;
try {
if (!c.isClosed()) {
c.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public void close(ResultSet rs,
Connection conn) {
close(rs);
close(conn);
}
}
使用方式:暂定3种(1、指定单个表来生成;2、指定多个表来生成;3、获取该数据库中的所有表),里面的DataSource自己看着办,普通的直接spring自动注入就好,多数据自己看着是用什么框架来动态切换。
String handleType = (String) data.get("handleType");
if (StringUtils.isEmpty(handleType)) {
return ResponseResult.createFailResult("缺少操作类型", null);
}
String datasource = (String) data.get("datasource");
if (StringUtils.isEmpty(datasource)) {
return ResponseResult.createFailResult("缺少datasource", null);
}
String fileUrl = (String) data.get("fileUrl");
if (StringUtils.isEmpty(fileUrl)) {
return ResponseResult.createFailResult("缺少fileUrl", null);
}
//指定数据库
String database = (String) data.get("database");
if (StringUtils.isEmpty(database)) {
return ResponseResult.createFailResult("缺少database", null);
}
List tables = new ArrayList<>();
switch (handleType){
case "one":
//指定单个表来生成
String datatable = (String) data.get("datatable");
if (StringUtils.isEmpty(datatable)) {
return ResponseResult.createFailResult("缺少datatable", null);
}
tables.add(datatable);
break;
case "list":
//指定多个表来生成
String datatables = (String) data.get("datatables");
if (StringUtils.isEmpty(datatables)) {
return ResponseResult.createFailResult("缺少datatables", null);
}
String[] tempList = datatables.split(",");
if(tempList.length<1){
return ResponseResult.createFailResult("解析后的数据库列表为空", null);
}
tables.addAll(Arrays.asList(tempList));
break;
case "database":
//获取该数据库中的所有表
try {
DynamicDataSourceContext.set(datasource);
List tableList = DataDictionaryCreateUtil.getInstance().getDatatable(dataSource,database);
if(tableList==null||tableList.size()<1){
return ResponseResult.createFailResult("查询后发现该数据库下没有表", null);
}
tables.addAll(tableList);
} catch (Exception e) {
e.printStackTrace();
return ResponseResult.createFailResult("操作失败:" + e.getMessage(), null);
}
break;
// case "datasource":
// //获取该数据源所有的数据库中的所有表
default:
return ResponseResult.createFailResult("没有对应的操作类型", null);
}
try {
//多数据源切换
DynamicDataSourceContext.set(datasource);
DataDictionaryCreateUtil.getInstance().saveTables(fileUrl,dataSource,database,tables);
} catch (Exception e) {
e.printStackTrace();
return ResponseResult.createFailResult("操作失败:" + e.getMessage(), null);
}
最终的界面(当然自己去改html,或者换一个方式显示也可以,主要这个不是给我自己看,懒得搞那么好看了哈)