Skip to content
Java jdbc 7 min read

DatabaseMetaData

DatabaseMetaData is a JDBC interface that lets you ask the database itself what it supports and what it contains — all at runtime, without reading any config files or writing database-specific code. You can list every table in a schema, check whether the driver supports stored procedures, discover primary and foreign keys, and much more.

Getting a DatabaseMetaData Object

You retrieve DatabaseMetaData directly from an open Connection:

import java.sql.*;

public class DatabaseMetaDataBasic {
    public static void main(String[] args) throws Exception {
        String url = "jdbc:mysql://localhost:3306/school";

        try (Connection conn = DriverManager.getConnection(url, "root", "secret")) {
            DatabaseMetaData meta = conn.getMetaData();

            System.out.println("Database: " + meta.getDatabaseProductName());
            System.out.println("Version : " + meta.getDatabaseProductVersion());
            System.out.println("Driver  : " + meta.getDriverName());
            System.out.println("JDBC URL: " + meta.getURL());
            System.out.println("User    : " + meta.getUserName());
        }
    }
}

Output:

Database: MySQL
Version : 8.0.33
Driver  : MySQL Connector/J
JDBC URL: jdbc:mysql://localhost:3306/school
User    : root@localhost

Note: DatabaseMetaData is a snapshot taken at the moment getMetaData() is called. It stays valid as long as the Connection is open; close the connection and the object becomes unusable.

Database and Driver Information

DatabaseMetaData exposes dozens of methods for discovering general information about the database engine and the JDBC driver in use:

MethodWhat it returns
getDatabaseProductName()Database engine name ("MySQL", "PostgreSQL", …)
getDatabaseProductVersion()Engine version string
getDatabaseMajorVersion()Major version as int
getDatabaseMinorVersion()Minor version as int
getDriverName()JDBC driver name
getDriverVersion()Driver version string
getJDBCMajorVersion()JDBC spec major version the driver implements
getJDBCMinorVersion()JDBC spec minor version
getURL()Connection URL used
getUserName()Connected database user
getMaxConnections()Max simultaneous connections (0 = no limit / unknown)
getDefaultTransactionIsolation()Default isolation level constant

Checking Feature Support

Before using an advanced feature, you can ask whether the database actually supports it:

DatabaseMetaData meta = conn.getMetaData();

System.out.println("Supports transactions      : " + meta.supportsTransactions());
System.out.println("Supports stored procedures : " + meta.supportsStoredProcedures());
System.out.println("Supports batch updates     : " + meta.supportsBatchUpdates());
System.out.println("Supports full outer joins  : " + meta.supportsFullOuterJoins());
System.out.println("Is read-only               : " + meta.isReadOnly());

Output:

Supports transactions      : true
Supports stored procedures : true
Supports batch updates     : true
Supports full outer joins  : false
Is read-only               : false

Tip: These boolean checks are invaluable when writing database-agnostic libraries. Instead of hardcoding “MySQL doesn’t do X”, you ask the driver at runtime.

Listing Catalogs and Schemas

Databases organize objects in a hierarchy: catalog → schema → table. DatabaseMetaData lets you explore each level.

Catalogs

In MySQL a catalog is the same as a database name. In other systems it maps to a top-level namespace:

import java.sql.*;

public class ListCatalogs {
    public static void main(String[] args) throws Exception {
        try (Connection conn = DriverManager.getConnection(
                "jdbc:mysql://localhost:3306/", "root", "secret")) {

            DatabaseMetaData meta = conn.getMetaData();

            try (ResultSet catalogs = meta.getCatalogs()) {
                System.out.println("Catalogs (databases):");
                while (catalogs.next()) {
                    System.out.println("  " + catalogs.getString("TABLE_CAT"));
                }
            }
        }
    }
}

Output:

Catalogs (databases):
  information_schema
  school
  sys

Schemas

Schemas sit one level below catalogs. PostgreSQL uses schemas heavily; MySQL treats them as an alias for catalogs:

try (ResultSet schemas = meta.getSchemas()) {
    while (schemas.next()) {
        System.out.println(schemas.getString("TABLE_SCHEM")
            + " / " + schemas.getString("TABLE_CAT"));
    }
}

Listing Tables

getTables() is one of the most-used methods. It accepts four filter parameters — catalog, schema pattern, table name pattern, and an array of table types — and returns a ResultSet with one row per match:

import java.sql.*;

public class ListTables {
    public static void main(String[] args) throws Exception {
        String url = "jdbc:mysql://localhost:3306/school";

        try (Connection conn = DriverManager.getConnection(url, "root", "secret")) {
            DatabaseMetaData meta = conn.getMetaData();

            // null wildcards mean "match everything"
            try (ResultSet tables = meta.getTables("school", null, "%", new String[]{"TABLE"})) {
                System.out.printf("%-30s %-15s%n", "Table Name", "Type");
                System.out.println("-".repeat(45));

                while (tables.next()) {
                    System.out.printf("%-30s %-15s%n",
                        tables.getString("TABLE_NAME"),
                        tables.getString("TABLE_TYPE"));
                }
            }
        }
    }
}

Output:

Table Name                     Type
---------------------------------------------
courses                        TABLE
enrollments                    TABLE
students                       TABLE

The full list of columns available in the returned ResultSet includes TABLE_CAT, TABLE_SCHEM, TABLE_NAME, TABLE_TYPE, and REMARKS.

Tip: Pass new String[]{"TABLE", "VIEW"} to include views in the results, or null to return everything (tables, views, system tables, synonyms, aliases).

Inspecting Columns

getColumns() returns metadata about every column in one or more tables — exactly what you need when building schema migration tools or ORM code generators:

import java.sql.*;

public class ListColumns {
    public static void main(String[] args) throws Exception {
        String url = "jdbc:mysql://localhost:3306/school";

        try (Connection conn = DriverManager.getConnection(url, "root", "secret")) {
            DatabaseMetaData meta = conn.getMetaData();

            try (ResultSet cols = meta.getColumns("school", null, "students", "%")) {
                System.out.printf("%-20s %-15s %-10s %-10s%n",
                    "Column", "Type", "Size", "Nullable");
                System.out.println("-".repeat(60));

                while (cols.next()) {
                    System.out.printf("%-20s %-15s %-10s %-10s%n",
                        cols.getString("COLUMN_NAME"),
                        cols.getString("TYPE_NAME"),
                        cols.getInt("COLUMN_SIZE"),
                        cols.getString("IS_NULLABLE"));
                }
            }
        }
    }
}

Output:

Column               Type            Size       Nullable
------------------------------------------------------------
id                   INT             11         NO
name                 VARCHAR         100        YES
grade                CHAR            1          YES
created_at           DATETIME        19         YES

Primary Keys and Foreign Keys

Primary Keys

try (ResultSet pk = meta.getPrimaryKeys("school", null, "enrollments")) {
    while (pk.next()) {
        System.out.println("PK column : " + pk.getString("COLUMN_NAME"));
        System.out.println("Key name  : " + pk.getString("PK_NAME"));
    }
}

Output:

PK column : id
Key name  : PRIMARY

Foreign Keys

getImportedKeys() tells you which columns in the given table reference another table (i.e., incoming foreign keys from the table’s perspective):

try (ResultSet fk = meta.getImportedKeys("school", null, "enrollments")) {
    while (fk.next()) {
        System.out.printf("FK: %s.%s → %s.%s%n",
            fk.getString("FKTABLE_NAME"),
            fk.getString("FKCOLUMN_NAME"),
            fk.getString("PKTABLE_NAME"),
            fk.getString("PKCOLUMN_NAME"));
    }
}

Output:

FK: enrollments.student_id → students.id
FK: enrollments.course_id  → courses.id

Use getExportedKeys() for the reverse direction — which other tables reference a given table.

Discovering Indexes

getIndexInfo() reveals every index on a table, including whether it is unique:

try (ResultSet idx = meta.getIndexInfo("school", null, "students", false, false)) {
    while (idx.next()) {
        System.out.printf("Index: %-20s  Column: %-15s  Unique: %b%n",
            idx.getString("INDEX_NAME"),
            idx.getString("COLUMN_NAME"),
            !idx.getBoolean("NON_UNIQUE"));
    }
}

Output:

Index: PRIMARY               Column: id               Unique: true
Index: idx_name              Column: name             Unique: false

Stored Procedures

You can enumerate stored procedures in the database without reading any DDL scripts:

try (ResultSet procs = meta.getProcedures("school", null, "%")) {
    while (procs.next()) {
        System.out.println("Procedure: " + procs.getString("PROCEDURE_NAME"));
    }
}

To inspect the parameters of a specific procedure, use getProcedureColumns().

SQL Keywords and Limits

A few methods help you stay within database-specific limits when building dynamic SQL:

DatabaseMetaData meta = conn.getMetaData();

System.out.println("SQL keywords       : " + meta.getSQLKeywords());
System.out.println("Max table name len : " + meta.getMaxTableNameLength());
System.out.println("Max column name len: " + meta.getMaxColumnNameLength());
System.out.println("String functions   : " + meta.getStringFunctions());
System.out.println("Numeric functions  : " + meta.getNumericFunctions());

getSQLKeywords() returns a comma-separated list of keywords that are not in SQL-92 but are reserved by this database — useful for quoting identifiers correctly.

Under the Hood

DatabaseMetaData is implemented entirely inside the JDBC driver, not in the JDK. When you call conn.getMetaData(), the driver returns its own implementation of the interface (e.g., com.mysql.cj.jdbc.DatabaseMetaDataUsingInfoSchema). Most methods issue a query against the database’s information schema — a set of system views (INFORMATION_SCHEMA.TABLES, INFORMATION_SCHEMA.COLUMNS, etc.) — to answer your questions.

Key points for experienced readers:

  • Each call may hit the database. Unlike ResultSetMetaData, which reads data the driver already has from the query response, DatabaseMetaData method calls like getTables() and getColumns() often execute a real SQL query against the information schema. Cache results if you call them in a tight loop.
  • Pattern arguments use SQL wildcards. The % wildcard means “match any sequence of characters” and _ matches a single character — the same as LIKE in SQL. Pass null to skip filtering on that parameter entirely.
  • Driver quality varies. The JDBC spec defines all these methods, but some drivers implement only a subset. Always test with your actual database and driver version, especially for methods like getProcedures() or getExportedKeys().
  • Transaction isolation constants. getDefaultTransactionIsolation() returns one of the Connection.TRANSACTION_* constants. You can compare this against Connection.TRANSACTION_READ_COMMITTED etc. to understand the default behavior before opening any transaction.
  • Connection is still needed. DatabaseMetaData does not hold a standalone connection — it references the one you obtained it from. If you close the connection, any pending ResultSet from a metadata call becomes invalid.

Warning: Never call meta.getTables() or meta.getColumns() inside a hot path (e.g., once per request in a web application). Cache schema information at startup or use a background refresh strategy.

  • ResultSetMetaData — column-level metadata for a specific query result, complementing database-level metadata
  • ResultSet — iterate over query rows; DatabaseMetaData helps you understand the structure those rows come from
  • Connection Interface — where you call getMetaData() from; manages transactions and session state
  • PreparedStatement — parameterised queries whose input types can be explored via ParameterMetaData
  • JDBC Steps — end-to-end walkthrough of connecting to a database and executing queries
  • Statement — the basic interface for running SQL; pairs naturally with schema discovery from DatabaseMetaData
Last updated June 13, 2026
Was this helpful?