Internals of a DriverManager

0

In this article we delve into the details of a DriverManager and how it manages to locate the appropriate Driver class to establish database connection.

Database connection

The database driver is responsible to establish connection to the database. An application should not instantiate a driver class directly and instead should let the DriverManager return a Connection.
Connection con = DriverManager.getConnection(url)

The URL passed in must contain the necessary details for the DriverManager to select the driver from the set of registered drivers.
In the below class diagram, we have three driver classes (DriverOne, DriverTwo and DriverThree).
DriverManager
DriverManager

Registering a driver

In order for the driver to be available to DriverManager, it must be registered. Once registered, it gets added to the internal driver cache of DriverManager.

DriverManager.registerDriver(driverInstance);

Service provider mechanism

Read ServiceLoader to know how service provider mechanism works.
The implementation(s) of Driver are added to the configuration file META-INF/services/java.sql.Driver. Note that the name of the configuration file must be the fully-qualified name of the Driver. When DriverManager is called first time, it finds all the copies of META-INF/services/java.sql.Driver in the classpath. It then loads and instantiates the driver implementations listed in the configuration file.

When a Driver class is loaded, it should create an instance of¬†itself and register it with the DriverManager¬†else it won’t be available in the drivers cache of DriverManager.

public class DriverOne implements Driver {
    
    private static Driver INSTANCE = new DriverOne();
    
    static {
        try {
            DriverManager.registerDriver(INSTANCE);
        } catch (SQLException e) {
            //log error
        }
    }
...
}

Selecting a driver

When we request a Connection from DriverManager, it will iterate over all registered drivers asking them for a connection.The driver must implement acceptsURL(url), to make sure the right driver is selected for the JDBC URL.
If it doesn’t support the protocol, it must return null so that the DriverManager can try connection with the next driver.
In case of h2 database, the URL must start with jdbc:h2:.
If it does support the protocol it will either return an established connection, or it will throw an SQLException.
If URL is jdbc:default:connection, it will return the default connection.

DriverOne

public class DriverOne implements Driver {
    private static Driver INSTANCE = new DriverOne();
    static {
        try {
            DriverManager.registerDriver(INSTANCE);
        } catch (SQLException e) {
            //log error
        }
    }
    @Override
    public Connection connect(String url, Properties info) throws SQLException {
        if (!acceptsURL(url)) {
            return null;
        }
        return DEFAULT_CONNECTION;
    }
    @Override
    public boolean acceptsURL(String url) throws SQLException {
        return url.startsWith("jdbc:driverOne:");
    }
...
}

DriverTwo

public class DriverTwo implements Driver {
..
    static {
        try {
            DriverManager.registerDriver(INSTANCE);
        } catch (SQLException e) {
            //log error
        }
    }
..
}

Suppose we register DriverOne and DriverTwo, both will be available in the cache of DriverManager. We won’t register DriverThree so it won’t be available in the cache.

public void testRegisteredDrivers() {
        Enumeration drivers = DriverManager.getDrivers();
        while(drivers.hasMoreElements()) {
            Driver driver = drivers.nextElement();
            assertTrue(driver instanceof JdbcOdbcDriver || 
                    driver instanceof DriverOne || 
                    driver instanceof DriverTwo);
        }
    }
Test case to get DriverOne.
    public void testDriverOne() throws SQLException {
        Driver driverOne = DriverManager.getDriver("jdbc:driverOne:");
        assertTrue(driverOne instanceof DriverOne);
    }

SQLException

If URL is null or there is an error in the URL, an SQLException will be thrown.If all drivers return null, that is none support the protocol, an SQLException will be thrown with error “No suitable driver found for ${url}”
public void testNoSuitableDriverSqlException() {
        try {
            DriverManager.getDriver("invalid");
            fail("No driver for this URL, should have thrown exception!");
        } catch (SQLException e) {
            //expected
        }
}
Share.

Leave A Reply