java 代码生成数据字典

这是一个用于生成数据库数据字典的工具,通过DataSource获取表信息,支持多数据源。工具能自动化获取表名、表备注、列名、列类型、是否可为空等信息,并生成HTML报告。使用时需指定操作类型(单表、多表或全库)、数据源、数据库名和输出文件路径。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

交接项目要给数据字典,项目又是中途接手,本来就没有,设计的又没做。搞个小工具,方便一下。

 

原理:通过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 = "<!DOCTYPE html>\n" +
            "<html>\n" +
            "<head>\n" +
            "<title></title>\n" +
            "<meta charset=\"UTF-8\">\n" +
            "</head>\n" +
            "<body>\n";
    /**
     * html模板body后缀
     */
    String templateHtmlSuffix =
            "</body>\n" +
            "</html>";
    /**
     * html 内容表格模板前缀
     */
    String templateHtmlContentPrefix ="<table border=\"1\" style=\"border-collapse:collapse\">\n<tr><th>字段名</th><th>是否必填</th><th>数据类型</th><th>注释</th>\n";
    /**
     * html 内容表格模板前缀
     */
    String templateHtmlContentSuffix ="</table>\n";
    /**
     * html 内容表格模板
     */
    String templateHtmlContent = "</tr><tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>\n";


    /**
     * 获取数据库下面所有表
     * @param dataSource
     * @param database
     * @return
     * @throws Exception
     */
    public List<String> 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<String> 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<String> tables) throws Exception {
        Connection conn = dataSource.getConnection();
        ResultSet rs = null;
        StringBuffer sb = new StringBuffer();
        try {
            for (String table : tables) {

                //表名备注
                String tableNameRemark = null;
                List<DataDictionaryInfo> 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("<h3>表名:" + list.get(0).getTableName() + "(" + tableNameRemark + ")" + ",模式:" + list.get(0).getTableSchemaName() + "</h3>").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("<hr/>").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<String> 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<String> 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,或者换一个方式显示也可以,主要这个不是给我自己看,懒得搞那么好看了哈)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值