1. Introduction

JDBC is a set of specifications defining the API and SPI parts of the contract for Java Database Connectivity. The standard defines the JDBC driver abstraction as the primary entry point to interact with a database.

In this tutorial, we’ll take a look at some of the basic steps needed to load JDBC drivers.

2. JDBC Drivers

To connect to a database, we must get an instance of a JDBC driver.

We can obtain it through the DriverManager by specifying the JDBC URL connection string. Such a URL contains the type of database engine, database name, hostname, and port, as well as other connection parameters that are specific to the database vendor.

Using the connection string, we can obtain a database connection object, which is the foundational unit of communication with the database in JDBC:

Connection con = DriverManager.getConnection(
   "jdbc:postgresql://localhost:21500/test?user=fred&password=secret&ssl=true");

How does the driver manager know which driver to use if the only indication is the specified URL?

There may be many JDBC drivers on the classpath, so there must be a way to distinguish each driver uniquely.

3. Legacy Approach

Before JDBC version 4 and Java SE 1.6, there was no generic mechanism in the JVM that would enable services to be discovered and registered automatically. Because of that, a manual step was needed to load the JDBC driver class by name:

Class.forName("oracle.jdbc.driver.OracleDriver");

The class loading process triggers a static initialization routine that registers the driver instance with the DriverManager and associates this class with the database engine identifier, such as oracle or postgres.

After the registration is complete, we can use this identifier inside the JDBC URL as jdbc:oracle.

A typical driver registration routine will instantiate the driver instance and pass it over to the DriverManager.registerDriver method:

public static void register() throws SQLException {
    if (isRegistered()) {
        throw new IllegalStateException("Driver is already registered. It can only be registered once.");
    } else {
        Driver registeredDriver = new Driver();
        DriverManager.registerDriver(registeredDriver);
        Driver.registeredDriver = registeredDriver;
    }
}

The example above shows the Postgres JDBC driver registration with the DriverManager. It is triggered by the JVM as part of the static initializer.

It is possible to partially automate this step even with the legacy approach by setting the jdbc.drivers system property:

java -Djdbc.drivers=oracle.jdbc.driver.OracleDriver

When this property is specified, the driver manager will automatically attempt to load the specified JDBC driver.

4. JDBC 4 Approach

The problem of automatic service discovery was solved with Java 1.6 and the service provider mechanism. It enables service providers to declare their services by placing them under META-INF/services inside the JAR file containing the services.

This mechanism registers the driver automatically so that the manual step to load the class is no longer necessary. However, even with the service provider in place, manual class loading will not cause a failure. It is perfectly legal to invoke driver loading explicitly with recent JVMs and JDBC 4 drivers.

The service provider specification simply replaces manual class loading with a declarative approach. For example, the PostgreSQL JDBC driver has a single file under META-INF/services/. The file name is java.sql.Driver (which is a well-established convention for JDBC drivers). It contains the fully qualified class name of the JDBC driver, which, in this case, is org.postgresql.Driver.

5. Conclusion

In this article, we’ve reviewed basic concepts around JDBC, as well as various methods to load JDBC drivers, with an explanation of each approach.

As usual, the complete source code for the article is available over on GitHub.