为什么 Informix JDBC 驱动程序处理不相关的连接字符串?

Why does the Informix JDBC driver handle unrelated connection strings?

当 Informix JDBC 驱动程序出现在我的 class 路径中时,它似乎在适当的驱动程序获得机会之前拦截并拒绝所有连接字符串。

例如,像 jdbc:ghmghmghm 这样完全没有意义的连接字符串将导致以下堆栈跟踪:

java.sql.SQLException: Invalid sub-protocol Invalid sub-protocol: 'ghmghmghm'
    at com.informix.util.IfxErrMsg.getLocSQLException(IfxErrMsg.java:493)
    at com.informix.jdbc.IfxDriver.checkURL(IfxDriver.java:560)
    at com.informix.jdbc.IfxDriver.connect(IfxDriver.java:208)
    at java.sql.DriverManager.getConnection(DriverManager.java:664)
    at java.sql.DriverManager.getConnection(DriverManager.java:208)

我的理解是,行为良好的 JDBC 驱动程序将自己限制为以其魔法前缀开头的连接字符串。是 Informix 驱动程序坏了,还是我有不合理的期望?

更新

如果我只删除 Informix 驱动程序,但保留所有其他驱动程序,则异常会翻转到更正常的状态

java.sql.SQLException: No suitable driver found for jdbc:ghmghmghm
    at java.sql.DriverManager.getConnection(DriverManager.java:596)
    at java.sql.DriverManager.getConnection(DriverManager.java:187)

此外,如果 Informix 驱动程序被删除,则存在特定的有效连接字符串 jdbc:sybase:Tds:leeta:5001/leeta_ase1,但如果存在 Informix,则会失败(Informix 无效子协议堆栈跟踪)。

我的结论是 Informix 没有正确地拒绝完全不匹配的连接字符串,并且 Informix 正在首先破解 Sybase 连接字符串(但不是我尝试过的所有其他连接字符串类型...)

我的LinuxJDK是

java version "1.7.0_91"
OpenJDK Runtime Environment (IcedTea 2.6.3) (7u91-2.6.3-0ubuntu0.14.04.1)
OpenJDK 64-Bit Server VM (build 24.91-b01, mixed mode)

但我也看到错误的连接字符串导致官方 Java 8 在 Windows 上出现 Infx 跟踪。从未见过 Sybase 在 Windows 上被拦截,但也许这是一个 class 路径排序问题。

更新 2

我无法重复我关于完全有效的连接字符串被 Informix 拦截和拒绝的说法。我一定是一直在尝试微妙的坏字符串,看到 Informix 堆栈,删除 Informix 驱动程序作为响应,然后认为来自正确驱动程序的堆栈是成功的(因为它允许快速修复连接字符串)。

我看到了一些改进方法:

感谢所有反馈!

根据 DriverManager 文档:

As part of its initialization, the DriverManager class will attempt to load the driver classes referenced in the "jdbc.drivers" system property. This allows a user to customize the JDBC Drivers used by their applications. For example in your ~/.hotjava/properties file you might specify:

jdbc.drivers=foo.bah.Driver:wombat.sql.Driver:bad.taste.ourDriver

所以,假设您有 3 个 JDBC 驱动程序,那么您的 jdbc.drivers 属性 看起来像这样:

jdbc.drivers=com.DriverA:com.DriverB:com.DriverC

如果您调用 DriverManager.getConnection("jdbc:driverA:blahblah");,DriverManager 不知道要使用 jdbc.drivers 属性 中的哪个驱动程序,因此它必须遍历所有驱动程序。

DriverManager.getConnection() 可能会做类似的事情:

public Connection getConnection(String url) {

    Set<Driver> drivers = // drivers in 'jdbc.drivers' prop

    SQLException failure = null;

    for(Driver driver : drivers) {
        try {
            Connection conn = driver.connect(url);
            if(conn != null)
                return conn;
        } catch (SQLException sqle) {
            // potentially not trying to connect to the right driver
            if(failure == null)
                failure = sqle;
        }
    }

    // If we get here, no drivers could connect
    if(failure != null)
        throw failure;
    else // no connection obtained, but no drivers complained
        throw new SQLException("No driver found for URL " + url);
}

更新: (代码示例已更新)

OpenJDK 似乎可以容忍驱动程序 returns 对于它无法识别的协议为 null 的情况,并且它还可以容忍驱动程序为它不识别的协议抛出异常的情况。不认识。

我认为 OpenJDK 永远不应抛出直接来自驱动程序的异常,并且如果未找到 URL.

的驱动程序,则始终抛出新的 SQLException

这听起来像是 Informix 驱动程序中的错误(但充其量只是一个小错误)。 well-behaved JDBC 驱动程序需要遵循 java.sql.Driver.connect(String url, Properties properties) 中定义的期望(强调我的):

Attempts to make a database connection to the given URL. The driver should return "null" if it realizes it is the wrong kind of driver to connect to the given URL. This will be common, as when the JDBC driver manager is asked to connect to a given URL it passes the URL to each loaded driver in turn.

The driver should throw an SQLException if it is the right driver to connect to the given URL but has trouble connecting to the database.

java.sql.DriverManager.getConnection会一一查询所有注册驱动的connect方法。如果驱动程序 returns null 它将继续下一个驱动程序。如果驱动程序 return 有一个连接,则该连接会 return 发送给调用者。如果所有驱动程序 return null,则会抛出 SQLException"No suitable driver found for [url]" 消息。

如果一个驱动程序抛出SQLException,最后抛出的异常被保留,驱动程序管理器将继续处理下一个驱动程序。如果所有其他驱动程序拒绝与 null 的连接尝试,则将抛出最后一个异常,而不是 "No suitable driver..." 异常。据我所知,在旧的 Java 版本中,它实际上会停止尝试其他驱动程序。但是 DriverManager 的代码(至少从 Oracle/Sun Java 5 开始)防止了这种情况,并防止行为不端的驱动程序垄断 JDBC,并允许多个驱动程序用于同一数据库(和协议)进行尝试。

因此,除非您的 Java 版本有不同的 DriverManager 实现(在第一个异常时停止),否则它应该继续使用其他已注册的驱动程序,如果其中任何一个接受 URL那么应该没有问题。

问题已在最新的 Informix JDBC 驱动程序版本 (JDBC.4.10.JC8DE) 中修复。