Redshift 和 Postgres JDBC 驱动程序都拦截 jdbc://postgresql 连接字符串

Redshift and Postgres JDBC driver both intercept jdbc://postgresql connection string

我有一个不太确定如何解决的问题:我有一个 Web 应用程序(打包为 war),客户端可以配置他们希望指向的数据库。我们支持 PostgreSQL 和 Redshift(以及其他)。 JDBC自动加载了4个驱动,不错。问题是:

看来 Redshift JDBC 驱动程序会先于 PostgreSQL 响应 jdbc://postgresql 连接字符串。这会在连接到 PostgreSQL 数据库时导致 JDBC 错误。

我将驱动程序名称 'org.postgresql.Driver' 指定为 pom.xml 中数据源的驱动程序,但我不确定 spring JDBC 模板是怎样的选择驱动程序(除非它选择第一个处理程序)。

还有其他人 运行 遇到过此类问题吗?

这是因为 redshift 驱动程序注册为能够处理 jdbc:postgresqljdbc:redshift URL 前缀。

当 Postgres 和 Redshift 驱动程序从它们的 jar 中加载时,它们各自向 DriverManger 注册。

DriverMananger.getDriver()DriverManager.getConnection() 中实现的逻辑是遍历每个驱动程序并在驱动程序表明它能够处理给定的 URL 后停止。

如果 Postgres 驱动程序先注册,则一切正常,因为 Postgres 驱动程序仅尝试处理 jdbc:postgresql。如果 Redshift 驱动程序首先成功注册,那么将永远不会使用 Postgres 驱动程序。

我想出解决这个问题的唯一方法是添加:

static {
    // Put the redshift driver at the end so that it doesn't
    // conflict with postgres queries
    java.util.Enumeration<Driver> drivers =  DriverManager.getDrivers();
    while (drivers.hasMoreElements()) {
        Driver d = drivers.nextElement();
        if (d.getClass().getName().equals("com.amazon.redshift.jdbc41.Driver")) {
            try {
                DriverManager.deregisterDriver(d);
                DriverManager.registerDriver(d);
            } catch (SQLException e) {
                throw new RuntimeException("Could not deregister redshift driver");
            }
            break;
        }
    }
}

另一种解决方案是将 "OpenSourceSubProtocolOverride=true" 添加到 JDBC 常规 PostgreSQL 连接的连接字符串。

示例:

jdbc:postgresql://localhost:5432/postgres?OpenSourceSubProtocolOverride=true  

太糟糕了,在我解决这个问题之前我没有看到其他答案,但我通过这样做覆盖了 Redshift 驱动程序的“postgres”子协议来解决了这个问题

String[] names = com.amazon.redshift.PGInfo.PG_SUBPROTOCOL_NAMES;
for (int i = 0; i < names.length; i++)
    if (names[i].equals("postgresql"))
        names[i] = UUID.randomUUID().toString(); // Just random string