使用 DatabaseMetaData 接口和 ResultSetMetaData 查询数据库结构

概述

在 JDBC 技术规范中,提供了 Connection,Statement,ResultSet 这三个开发过程中经常用到的接口。针对与每个接口,JDBC 规范提供了相应的接口描述对象,也就是 xxxMetaData 系列描述对象。DatabaseMetaDataResultSetMetaData 就是两个常用的获取数据库元数据相关信息的接口。

DatabaseMetaData

首先是 DatabaseMetaData 接口。

DatabaseMetaData接口常用的方法

  • ResultSet getTables(String catalog,String schemaPattern,String tableNamePattern,String[] types); //获取表信息
  • ResultSet getPrimaryKeys(String catalog,String schema,String table); //获取表主键信息
  • ResultSet getIndexInfo(String catalog,String schema,String table,boolean unique,boolean approximate); //获取表索引信息
  • ResultSet getColumns(String catalog,String schemaPattern,String tableNamePattern,String columnNamePattern); //获取表列信息
public static void main(String[] args)
{
    // 这里没有指定数据库
    String url = "jdbc:mysql://127.0.0.1:3306/";
    String user = "root";
    String pass = "root";
    try
    {
        Class.forName("com.mysql.jdbc.Driver").newInstance();
        Connection conn = DriverManager.getConnection(url, user, pass);
        DatabaseMetaData metadata = conn.getMetaData();
        System.out.println("数据库已知的用户: "+ metadata.getUserName());
        System.out.println("数据库的系统函数的逗号分隔列表: "+ metadata.getSystemFunctions());
        System.out.println("数据库的时间和日期函数的逗号分隔列表: "+ metadata.getTimeDateFunctions());
        System.out.println("数据库的字符串函数的逗号分隔列表: "+ metadata.getStringFunctions());
        System.out.println("数据库供应商用于 'schema' 的首选术语: "+ metadata.getSchemaTerm());
        System.out.println("数据库URL: " + metadata.getURL());
        System.out.println("是否允许只读:" + metadata.isReadOnly());
        System.out.println("数据库的产品名称:" + metadata.getDatabaseProductName());
        System.out.println("数据库的版本:" + metadata.getDatabaseProductVersion());
        System.out.println("驱动程序的名称:" + metadata.getDriverName());
        System.out.println("驱动程序的版本:" + metadata.getDriverVersion());

        System.out.println();
        System.out.println("数据库中使用的表类型");
        ResultSet rs = metadata.getTableTypes();
        while (rs.next())
        {
            System.out.println(rs.getString(1));
        }
        rs.close();

        System.out.println();

        /**  
         * 获取指定的数据库的所有表的类型,getTables()的第一个参数就是数据库名
         * 因为与MySQL连接时没有指定,这里加上,剩下的参数就可以为null了
         * 第二个参数是模式名称的模式,但是输出也是什么都没有。
         */  
        System.out.println("获取指定的数据库的所有表的类型");
        ResultSet rs1 = metadata.getTables("test", null, null, null);
        while (rs1.next())
        {
            System.out.println();
            System.out.println("数据库名:"+ rs1.getString(1));
            System.out.println("表名: "+rs1.getString(3));
            System.out.println("类型: "+rs1.getString(4));
        }
        rs1.close();

        System.out.println();
        System.out.println("获取指定的数据库的表的主键");
        // 获取指定的数据库的表的主键,第二个参数也是模式名称的模式,使用null了
        ResultSet rs2 = metadata.getPrimaryKeys("mysql", null, "db");
        while (rs2.next())
        {
            System.out.println("主键名称: "+ rs2.getString(4));
        }
        rs2.close();

        System.out.println();
        System.out.println("DatabaseMetaData.getIndexInfo()方法返回信息:");
        ResultSet rs3 = metadata.getIndexInfo("test", null, "user", false, true);
        while (rs3.next())
        {
            System.out.println("数据库名: "+ rs3.getString(1));
            System.out.println("表模式: "+ rs3.getString(2));
            System.out.println("表名称: "+ rs3.getString(3));
            System.out.println("索引值是否可以不唯一: "+ rs3.getString(4));
            System.out.println("索引类别: "+ rs3.getString(5));
            System.out.println("索引名称: "+ rs3.getString(6));
            System.out.println("索引类型: "+ rs3.getString(7));
            System.out.println("索引中的列序列号: "+ rs3.getString(8));
            System.out.println("列名称: "+ rs3.getString(9));
            System.out.println("列排序序列: "+ rs3.getString(10));
            System.out.println("TYPE为 tableIndexStatistic时它是表中的行数否则它是索引中唯一值的数量: "+ rs3.getString(11));
            System.out.println("TYPE为 tableIndexStatisic时它是用于表的页数否则它是用于当前索引的页数: "+ rs3.getString(12));
            System.out.println("过滤器条件: "+ rs3.getString(13));
        }   
        rs3.close();
    }
    catch (Exception e)
    {
        e.printStackTrace();
    }
}

ResultSetMetaData

ResultSetMetaData 接口,用于获取关于 ResultSet 对象中列的类型和属性信息的对象。

public static void main(String[] args)
{
    try
    {
        Class.forName("com.mysql.jdbc.Driver").newInstance();
        Connection conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/test","root","root");
        PreparedStatement ps = conn.prepareStatement("select * from bbs");
        ResultSet rs = ps.executeQuery();
        ResultSetMetaData rsme = rs.getMetaData();

        int columnCount = rsme.getColumnCount();
        System.out.println("ResultSet对象中的列数"+ columnCount);
        for (int i = 1; i < columnCount ; i++)
        {
            System.out.println();
            System.out.println("列名称: "+ rsme.getColumnName(i));
            System.out.println("列类型(DB): " + rsme.getColumnTypeName(i));
            System.out.println("长度: "+ rsme.getPrecision(i) );
            System.out.println("是否自动编号: "+ rsme.isAutoIncrement(i));
            System.out.println("是否可以为空: "+ rsme.isNullable(i));
            System.out.println("是否可以写入: "+ rsme.isReadOnly(i));
        }
    } catch (Exception e)
    {
        e.printStackTrace();
    }   
}

一个综合的运用

获取某数据库下所有表;对于每张表,获取其所有主键,获得其所有字段的部分信息,包括字段名称、字段SQL类型和字段JAVA类名称。

import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * DatabaseMetaData and ResultSetMetaData demo
 * 
 * @author liuqianfei
 *
 */
public class MetaDemo
{
    public static void main(String[] args)
    {
        try
        {
            String url = "jdbc:mysql://localhost:3306/world";
            String name = "root";
            String password = "root";
            Class.forName("com.mysql.jdbc.Driver");
            Connection conn = DriverManager.getConnection(url, name, password);
            DatabaseMetaData dbMeta = conn.getMetaData();

            // 取表名
            ResultSet rs = dbMeta.getTables(conn.getCatalog(), null, null, new String[] { "TABLE", "VIEW" });
            while (rs.next())
            {
                String tableName = rs.getString("TABLE_NAME");
                String remarks = rs.getString("REMARKS");
                System.out.println("=============================================");
                System.out.println("表名称:" + tableName + ", 表备注:" + remarks);

                // 取关键字
                ResultSet rs1 = dbMeta.getPrimaryKeys(conn.getCatalog(), null, tableName);
                while (rs1.next()) {
                    System.out.println("主键:" + rs1.getString("COLUMN_NAME"));
                }
                rs1.close();

                // 取字段
                Statement stm = conn.createStatement();
                String sql = "select * from " + tableName + " where 1=2";
                ResultSet rs2 = stm.executeQuery(sql);
                ResultSetMetaData rsmd = rs2.getMetaData();
                for (int i=1; i<=rsmd.getColumnCount(); i++) {

                    String colname = rsmd.getColumnName(i);
                    String colClassName = rsmd.getColumnClassName(i);
                    System.out.println("字段信息:" + colname + "->" + colClassName);
                    System.out.println("\t" + rsmd.getColumnType(i) + "=>" + rsmd.getColumnTypeName(i));
                }
                rs2.close();
            }
            rs.close();
        }
        catch (SQLException | ClassNotFoundException e)
        {
            Logger.getLogger(MetaDemo.class.getName()).log(Level.SEVERE, null, e);
        }
    }
}

Note

  • JDBC 元数据的操作是很消耗性能的,所以应尽量避免使用。
  • 在获取元数据中的 REMARK (即备注)前,需要设置 RemarksReporting 属性为 true,否则获取到的 REMARK 属性都是 null,设置该属性有两种方法:

方法一:

/**
 * 设置连接属性,使得可获取到表的REMARK(备注)
 */
Connection conn = getConnection();
conn.setRemarksReporting(true);
DatabaseMetaData dbmd = conn.getMetaData();

方法二:

/**
 * 设置可获取REMARK备注信息
 */
Properties props = new Properties(); 
props.put("remarksReporting", "true");
props.put("user", USER);
props.put("password", PASS);
conn =DriverManager.getConnection(URL, props);

API详细信息请参考官方文档,未尽之处欢迎留言讨论。

如果觉得这对你有用,请随意赞赏,给与作者支持
评论 0
最新评论