DatabaseMetaData in Java

Naveen AnamaneniUpdated · 6 min read

DatabaseMetaData provides information about the database as a whole, and about the driver you are connected through. You get it from a connection with con.getMetaData(). It can tell you the database product and version, which features it supports, and what it contains: the tables, their columns, primary keys and foreign keys. Tools depend on it: database browsers list tables with it, migration tools check whether a table already exists, and frameworks adapt their SQL to the database they find. In this lesson, you will learn its main methods and explore a small schema with them.

Methods: About the Product and Driver

MethodDescription
getDatabaseProductName(), getDatabaseProductVersion()They are used to get the database's name and version.
getDriverName(), getDriverVersion()They return the JDBC driver's name and version.
getJDBCMajorVersion(), getJDBCMinorVersion()They return the JDBC version the driver supports.
getURL(), getUserName()They return the connection's URL and user.

Methods: What the Database Supports

MethodDescription
supportsTransactions()It tells whether transactions are available.
supportsBatchUpdates()It tells whether batch updates work.
supportsSavepoints()It tells whether savepoints are available.
supportsStoredProcedures()It tells whether procedures can be called.
supportsResultSetType(type)It tells whether a result set type, such as scrollable, is supported.
getDefaultTransactionIsolation()It returns the default isolation level.

Methods: What the Database Contains

Each of these returns a ResultSet, with one row per item:

MethodRows describe
getTables(catalog, schema, namePattern, types)Tables and views
getColumns(catalog, schema, table, columnPattern)Columns of a table
getPrimaryKeys(catalog, schema, table)Primary key columns
getImportedKeys(catalog, schema, table)Foreign keys of this table, pointing to other tables
getExportedKeys(catalog, schema, table)Foreign keys in other tables that point to this one
getIndexInfo(...)Indexes
getSchemas(), getCatalogs()Schemas and catalogs (databases)

In the pattern arguments, % matches any text and null means "don't filter".

Example: Exploring a Schema

DbMeta.java▶ Try it
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
 
public class DbMeta {
    public static void main(String[] args) throws Exception {
        try (Connection con =
                 DriverManager.getConnection("jdbc:h2:mem:school");
             Statement st = con.createStatement()) {
            st.execute("CREATE TABLE course ("
                + "code VARCHAR(10) PRIMARY KEY, title VARCHAR(40) NOT NULL)");
            st.execute("CREATE TABLE student (id INT PRIMARY KEY, "
                + "name VARCHAR(50) NOT NULL, course VARCHAR(10), "
                + "FOREIGN KEY (course) REFERENCES course(code))");
 
            DatabaseMetaData md = con.getMetaData();
            System.out.println("Product: " + md.getDatabaseProductName());
            System.out.println("Driver:  " + md.getDriverName());
            System.out.println("JDBC:    " + md.getJDBCMajorVersion() + "."
                + md.getJDBCMinorVersion());
            System.out.println("Transactions? " + md.supportsTransactions());
            System.out.println("Batch updates? " + md.supportsBatchUpdates());
            System.out.println("Savepoints? " + md.supportsSavepoints());
 
            System.out.println("Tables:");
            try (ResultSet rs = md.getTables(
                    null, "PUBLIC", "%", new String[] {"TABLE"})) {
                while (rs.next()) {
                    System.out.println("  " + rs.getString("TABLE_NAME"));
                }
            }
            System.out.println("Columns of STUDENT:");
            try (ResultSet rs = md.getColumns(null, "PUBLIC", "STUDENT", "%")) {
                while (rs.next()) {
                    System.out.println("  " + rs.getString("COLUMN_NAME") + " "
                        + rs.getString("TYPE_NAME") + "("
                        + rs.getInt("COLUMN_SIZE") + ") nullable="
                        + rs.getString("IS_NULLABLE"));
                }
            }
            try (ResultSet rs = md.getPrimaryKeys(null, "PUBLIC", "STUDENT")) {
                rs.next();
                System.out.println("Primary key: "
                    + rs.getString("COLUMN_NAME"));
            }
            try (ResultSet rs = md.getImportedKeys(null, "PUBLIC", "STUDENT")) {
                rs.next();
                System.out.println("Foreign key: "
                    + rs.getString("FKCOLUMN_NAME") + " -> "
                    + rs.getString("PKTABLE_NAME") + "."
                    + rs.getString("PKCOLUMN_NAME"));
            }
        }
    }
}
Output
Product: H2
Driver:  H2 JDBC Driver
JDBC:    4.3
Transactions? true
Batch updates? true
Savepoints? true
Tables:
  COURSE
  STUDENT
Columns of STUDENT:
  ID INTEGER(32) nullable=NO
  NAME CHARACTER VARYING(50) nullable=NO
  COURSE CHARACTER VARYING(10) nullable=YES
Primary key: ID
Foreign key: COURSE -> COURSE.CODE

Explanation:

  1. The first lines identify the database and driver, and report three features as supported.
  2. getTables(null, "PUBLIC", "%", new String[] {"TABLE"}) asks for every ordinary table in the PUBLIC schema. Each row has a TABLE_NAME column, among others.
  3. getColumns returns one row per column of STUDENT, with its name, type, size and whether it accepts NULL.
  4. getPrimaryKeys reports that ID is the primary key.
  5. getImportedKeys reports the foreign key: STUDENT.COURSE refers to COURSE.CODE.

The column names of these result sets, such as TABLE_NAME, COLUMN_NAME and FKCOLUMN_NAME, are fixed by the JDBC specification, so the code works on any database. The values differ: H2 and Oracle store names in upper case, PostgreSQL in lower case, and the type names are each database's own.

ResultSetMetaData vs DatabaseMetaData

PointResultSetMetaDataDatabaseMetaData
Obtained fromresultSet.getMetaData()connection.getMetaData()
DescribesThe columns of one resultThe database and the driver
Typical question"What columns did this query return?""Which tables exist? Does this database support savepoints?"
ReturnsSimple valuesSimple values and result sets

Common Mistakes

Assuming name case. A table created as student may be reported as STUDENT or student depending on the database. Compare names case-insensitively, or use the case the database uses.

Passing the wrong schema. If nothing comes back, try null for the schema to search all of them.

Calling metadata methods in hot code. They are far more expensive than they look.

Interview Corner

What is DatabaseMetaData?

DatabaseMetaData, obtained from connection.getMetaData(), describes the database and driver: product name and version, driver name and version, the JDBC version, supported features (transactions, batch updates, savepoints, stored procedures, result set types), limits, and the database's structure through methods such as getTables, getColumns, getPrimaryKeys and getImportedKeys, which return result sets. It is used by database tools, migration utilities and frameworks that must adapt to different databases.

What is the difference between DatabaseMetaData and ResultSetMetaData?

DatabaseMetaData comes from a Connection and describes the whole database and its driver, including capabilities and schema objects. ResultSetMetaData comes from a ResultSet and describes only the columns of that particular result: their count, names, labels and types.

Practice

  1. Easy: Print the product name, product version and driver version of your connection.
  2. Harder: Write boolean tableExists(Connection con, String name) using getTables, comparing names without regard to case.

Quick check: DatabaseMetaData

4 questions
1How do you get a DatabaseMetaData?
2What does getTables return?
3Which method lists a table's foreign keys to other tables?
4What does % mean in a name pattern?

Key Takeaways

Was this tutorial helpful?

Lesson 495 of 578