Why use Class.forName when connecting to the database?

Asked

Viewed 2,621 times

10

For database connection, in addition to the inclusion of the JDBC driver, most articles and examples use the static method Class.forName(String). See the example with Mysql:

Class.forName("com.mysql.jdbc.Driver");

or even

Class.forName("com.mysql.jdbc.Driver").newInstance();

Some quotes:

What does the Class.forName(String) and the newInstance()? Unable to import package com.mysql.jdbc and instantiate an object Driver for the connection, for example?

  • 3

    This is no longer necessary since java 1.6. Previously it was necessary for java to find the correct driver. But after the version cited, the compiler itself already looks for the most suitable driver and if not, throws exception.

  • 1
  • 1

    @Almost Articuno. It’s not the compiler that does it. It’s the class DriverManager running time. The compiler itself does not want to know if the required class exists or not.

1 answer

9


TL;DR

  • It’s not necessary to use Class.forName(String) from Java 6, but doing so can still make your program refuse to boot if the driver is not in the classpath.

  • There is no need, and never has been, to use newInstance() in that case. It serves absolutely nothing.

  • You can import and instantiate the driver directly if you want without problems.


Follow the detailed explanations:

The Class.forName(String) is really necessary?

I’ll copy an excerpt of my other answer:

The driver boot process, using the Class.forName is no longer required in the latest versions of Java. However, in doing so, you immediately denounce the presence of eventual classpath errors. Also, it only needs to be done once, while loading the program, and if it fails, the program is hopelessly broken. Therefore, this process can be kept in a static boot block.

This mechanism was responsible for ensuring that the driver class was loaded. Once loaded, the class performs its own registration as an SQL driver and is recognized by the class DriverManager as such. From Java 6, you no longer need to use it.

A little bit of Reflection

For all classes loaded in the JVM, there is a class object java.lang.Class corresponding. To get yourself a class from its name, you can use the method Class.forName(String).

The newInstance(), is the method responsible for calling the constructor without parameters of a given class from the object Class corresponding. Once it invokes the constructor, it naturally returns the created instance. With this, it is possible to create instances of classes from the object Class.

Note that starting with Java 9, this method has become obsolete because it does not behave well in case the constructor throws an exception. The alternative is to trade newInstance() for getDeclaredConstructor().newInstance().

The class ServiceLoader

With the advent of ServiceLoader in Java 6, the explicit use of Class.forName(String) for this case became unnecessary, as the ServiceLoader is the mechanism that allows automatically enumerating services within Jars that implements a certain interface and it is this mechanism that drivers use to enumerate themselves without the programmer having to worry about it.

For example, if you open your mysql-connector-java-5.1.36.jar as if it were a ZIP file, you will find a file called java.sql.Driver inside a folder called META-INF/services. Here is the contents of this file:

com.mysql.jdbc.Driver
com.mysql.fabric.jdbc.FabricMySQLDriver

When the ServiceLoader for searching for interface implementations java.sql.Driver, he will search the archive META-INF/services/java.sql.Driver within each JAR, and in each JAR where this file is found, it will read a list with the name of the implementations. Then the ServiceLoader going load each of these classes and then, as you carry them, call the method newInstance(), keeping the instances produced in a Map.

However, how the ServiceLoader is used internally by the class DriverManager, you do not need to interact directly with it (nor need to know it exists) and no longer need to Class.forName(String) manually. The only thing you need to do is to use the DriverManager directly:

Connection con = DriverManager.getConnection(url, usuario, senha);

What the class of Driver does after all?

Let’s take a look at class source code com.mysql.jdbc.Driver:

package com.mysql.jdbc;

import java.sql.SQLException;

/**
 * The Java SQL framework allows for multiple database drivers. Each driver should supply a class that implements the Driver interface
 * 
 * <p>
 * The DriverManager will try to load as many drivers as it can find and then for any given connection request, it will ask each driver in turn to try to
 * connect to the target URL.
 * 
 * <p>
 * It is strongly recommended that each Driver class should be small and standalone so that the Driver class can be loaded and queried without bringing in vast
 * quantities of supporting code.
 * 
 * <p>
 * When a Driver class is loaded, it should create an instance of itself and register it with the DriverManager. This means that a user can load and register a
 * driver by doing Class.forName("foo.bah.Driver")
 */
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    //
    // Register ourselves with the DriverManager
    //
    static {
        try {
            java.sql.DriverManager.registerDriver(new Driver());
        } catch (SQLException E) {
            throw new RuntimeException("Can't register driver!");
        }
    }

    /**
     * Construct a new driver and register it with DriverManager
     * 
     * @throws SQLException
     *             if a database error occurs.
     */
    public Driver() throws SQLException {
        // Required for Class.forName().newInstance()
    }
}

Note that there is a static boot block that instantiates the class and registers it in the DriverManager. Thus, when the class is loaded, the required driver instance is already created. That is, when a Class.forName(String) is executed (either by you or indirectly by ServiceLoader), she will be instantiated.

I can call the newInstance() even so?

If you want to use the Class.forName(String) even so, by calling the newInstance(), you will create another instance that will not be registered on DriverManager and by not assigning it to anywhere, it will soon be discarded by the garbage collector. That is, the explicit call to newInstance() is totally useless and unnecessary.

However, having multiple driver instances is not harmful. In fact, one is registered in DriverManager by the static initializer and a second instance is created by ServiceLoader. If you use the newInstance(), will create a third instance as well (even more if you assign it to some variable and use it later).

It would be expected that the fact that there is more than one instance of the driver could cause problems, but it does not result due to how it is implemented. If you look at source code of the superclass of com.mysql.jdbc.Driver, which is the class com.mysql.jdbc.NonRegisteringDriver, you will see that all fields are static and shared by any instances that may exist. Therefore, if all instances share the same state and do not maintain any state in themselves, they all have the same behavior and operate on the same data.

And if I import the driver directly?

By importing the driver directly, if you can guarantee that your code will load it, its static initializer will run and it will register on DriverManager.

Use a com.mysql.jdbc.Driver.class in the middle of the code is not enough to ensure the loading of the class. But, if you use one of these things, then it works:

Class<?> x = com.mysql.jdbc.Driver.class;

Or else:

new com.mysql.jdbc.Driver();

That is, since you remember that there may be several equivalent driver instances out there, you can do it yes and there will be no problem.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.