【问题标题】:Dynamically load the JDBC driver动态加载 JDBC 驱动
【发布时间】:2013-01-23 11:41:33
【问题描述】:

我正在尝试使用这种代码动态加载 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 循环显示类路径的驱动程序:

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

第二个循环显示相同的驱动程序,但没有 MySQL 驱动程序。

我的问题是为什么?我错过了什么吗?

我读到in the JavaDoc of DriverManager,如果驱动程序已加载,每个驱动程序都会尝试通过驱动程序管理器注册自己。在我的代码中,这应该是loader.loadClass(drivername);。我认为这段代码应该调用静态部分,例如:

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

the Driver class。

【问题讨论】:

    标签: java jdbc classloader


    【解决方案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");
    

    【讨论】:

    • Euw,将动态加载的驱动程序置于更广泛的范围内。最好只构建 Driver 并使用它,而不用像 DriverManager 那样讨厌。
    • @TomHawtin-tackline 如果您知道该 URL 将用于什么 RDBMS,这是个好主意,但在某些情况下您可能不知道。
    【解决方案2】:

    您不能这样做,因为DriverManager 不允许您使用调用代码无权访问的驱动程序(即由不同类加载器加载的驱动程序):

    当调用getConnection 方法时,DriverManager 将尝试从初始化时加载的驱动程序和使用与当前小程序或应用程序相同的类加载器显式加载的驱动程序中找到合适的驱动程序 .

    据我所知,唯一可能的解决方法是手动实例化Driver,而不是使用DriverManager(假设它有一个无参数构造函数):

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

    虽然我不确定这是一个正确的方法。

    【讨论】:

    • 好吧,通过Constructor 因为Class.newInstance 很讨厌。
    • 我能否更详细地解释上述评论的含义?或者可能是解释的链接?
    • @JackJ Class.newInstance() 自 Java 9 以来已被弃用,这是有充分理由的。见docs.oracle.com/javase/9/docs/api/java/lang/…
    猜你喜欢
    • 2011-08-24
    • 1970-01-01
    • 2013-08-13
    • 2014-09-27
    • 2014-06-26
    • 1970-01-01
    • 2011-02-05
    • 1970-01-01
    相关资源
    最近更新 更多