Динамически загружать драйвер JDBC

Я пытаюсь загрузить драйвер JDBC динамически с помощью такого кода:

        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;
    }

Первый while-loop показывает драйверы пути к классам:

driver:[email protected]
driver:[email protected]
driver:[email protected]
driver:SQLServerDriver:1

а во втором цикле показаны одни и те же драйверы, но без драйвера MySQL.

Мой вопрос - почему? Я что-то пропустил?

Я читаю в JavaDoc DriverManager, что каждый драйвер пытается зарегистрировать себя drivermanager, если драйвер загружен. В моем коде это должно быть loader.loadClass(drivername);. Я думал, что этот код должен ссылаться на статическую часть, например:

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

класса Driver.

Ответ 1

что известная проблема с DriverManager и загрузчиками классов:

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

Определение драйвера (в основном делегат):

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....

Пример использования:

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");

Ответ 2

Вы не можете этого сделать, потому что DriverManager не позволяет использовать драйверы, к которым у вызывающего кода нет доступа to (т.е. драйверы, загруженные другим загрузчиком классов):

Когда вызывается метод getConnection, DriverManager будет пытаться найти подходящий драйвер из числа загруженных при инициализации, а те загружаются явно с использованием того же загрузчика классов, что и текущий апплет или приложение.

Насколько я знаю, единственным возможным обходным путем является создание экземпляра Driver вручную вместо использования DriverManager (при условии, что у него есть конструктор no-arg):

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

Хотя я не уверен, что это правильный подход.