15

I'm trying to load the JDBC driver dynamically with this kind of code:

        try{
        URL[] url={new URL("file:libs/mysql-connector-java-5.1.21.jar")};
        URLClassLoader loader = new URLClassLoader(url, System.class.getClassLoader());
        loader.loadClass(drivername);
        Enumeration<Driver> drivers = DriverManager.getDrivers();
        while(drivers.hasMoreElements()){
            Driver driver = drivers.nextElement();
            System.out.println("driver:"+driver);
        }
        Class.forName(drivername, true, loader);
        drivers = DriverManager.getDrivers();
        while(drivers.hasMoreElements()){
            Driver driver = drivers.nextElement();
            System.out.println("driver:"+driver);
        }
        Connection connect = DriverManager.getConnection(jdbcurl, user,
                password);

        return connect;
    }
    catch (MalformedURLException e){
        e.printStackTrace();
        return null;
    }

The first while-loop shows the drivers of the classpath:

driver:sun.jdbc.odbc.JdbcOdbcDriver@35712651
driver:oracle.jdbc.OracleDriver@58df0438
driver:com.ibm.db2.jcc.DB2Driver@525c7734
driver:SQLServerDriver:1

and the second loop shows the same drivers, but without the MySQL driver.

My Question is why? Did I miss something?

I read in the JavaDoc of DriverManager that every driver tries to register himself by the drivermanager, if the driver is loaded. In my Code this should be loader.loadClass(drivername);. I thought this code should invoke the static part for example:

static {
  try {
      java.sql.DriverManager.registerDriver(new Driver());
  } catch (SQLException E) {
      throw new RuntimeException("Can't register driver!");
  }
}

of the Driver class.

Joachim Sauer
  • 302,674
  • 57
  • 556
  • 614
bkb
  • 309
  • 2
  • 4
  • 12

2 Answers2

20

that's a known issue with DriverManager and classloaders, see:

http://www.kfu.com/~nsayer/Java/dyn-jdbc.html

Driver definition (basically a delegate):

class DriverShim implements Driver {
    private Driver driver;
    DriverShim(Driver d) { this.driver = d; }
    public boolean acceptsURL(String u) throws SQLException {
        return this.driver.acceptsURL(u);
    }
    public Connection connect(String u, Properties p) throws SQLException {
        return this.driver.connect(u, p);
    }
    // and so on....

Use example:

URL u = new URL("jar:file:/path/to/pgjdbc2.jar!/");
String classname = "org.postgresql.Driver";
URLClassLoader ucl = new URLClassLoader(new URL[] { u });
Driver d = (Driver)Class.forName(classname, true, ucl).newInstance();
DriverManager.registerDriver(new DriverShim(d));
DriverManager.getConnection("jdbc:postgresql://host/db", "user", "pw");
Cheeso
  • 189,189
  • 101
  • 473
  • 713
Riccardo Cossu
  • 2,699
  • 1
  • 28
  • 47
  • 1
    Euw, that puts a dynamically loaded driver into a far too broader scope. Better to just construct the `Driver` and use it without the nastiness of `DriverManager`. – Tom Hawtin - tackline Jan 24 '13 at 04:48
  • @TomHawtin-tackline that is a good idea if you know what RDBMS the URL will be for, but in some situations you may not know. – Lachlan Sep 18 '14 at 07:45
7

You cannot do it this way, because DriverManager doesn't allow you to use drivers that the calling code doesn't have access to (i.e. drivers loaded by different classloader):

When the method getConnection is called, the DriverManager will attempt to locate a suitable driver from amongst those loaded at initialization and those loaded explicitly using the same classloader as the current applet or application.

As far as I know the only possible workaround is to instantiate Driver manually instead of using DriverManager (assuming that it has a no-arg constructor):

Driver driver = Class.forName(drivername, true, loader).newInstance();
Connection connect = driver.connect(url, props);

Though I'm not sure that it's a correct approach.

Joachim Sauer
  • 302,674
  • 57
  • 556
  • 614
axtavt
  • 239,438
  • 41
  • 511
  • 482
  • 3
    Well, go through `Constructor` as `Class.newInstance` is nasty. – Tom Hawtin - tackline Jan 24 '13 at 04:44
  • Could I get a more elaborate explanation of what is meant by the comment above? Or perhaps a link to an explanation? – Jack J Jul 22 '20 at 01:38
  • 1
    @JackJ Class.newInstance() is deprecated since Java 9, for good reason. See https://docs.oracle.com/javase/9/docs/api/java/lang/Class.html#newInstance-- – A248 Aug 11 '20 at 18:52