使用了错误的 JDBC 驱动程序?
Wrong JDBC driver being used?
我有一个方法可以将记录插入 Postgres 数据库,returns 为该记录生成身份字段。问题是,如果我在我的 POM 文件中包含 Redshift 驱动程序,将使用该驱动程序而不是 Postgres 驱动程序 - 并且 Redshift 驱动程序不允许返回标识值。
密码是:
try {
Class.forName( "org.postgresql.Driver" ).newInstance();
Connection connection = DriverManager.getConnection( "jdbc:postgresql://localhost:5433/postgres", "postgres", "password" );
Statement stmt = connection.createStatement();
stmt.execute( "insert into public.job ( job_name ) values ( 'test' )" , Statement.RETURN_GENERATED_KEYS );
ResultSet keyset = stmt.getGeneratedKeys();
if ( keyset.next() ) System.out.println( keyset.getLong( 1 ) );
}
catch ( Exception e ) {
e.printStackTrace();
}
当使用这个 POM 时,它有效:
<dependencies>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>9.4-1201-jdbc41</version>
</dependency>
</dependencies>
当使用这个 POM 时,它不起作用:
<dependencies>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>redshift.jdbc</artifactId>
<version>1.1.2.0002</version>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>9.4-1201-jdbc41</version>
</dependency>
</dependencies>
是什么让 Java 选择 Redshift 驱动程序而不是 Postgres 驱动程序?
(Redshift驱动的类路径是com.amazon.jdbc41.Driver,所以我认为不是类路径冲突)
TIA
你的问题是,Java 支持 JDBC 4.0 的 ServiceLoader
机制。
在 JDBC 4 中,DriverManager
将从他们的 jar 文件中的 META-INF/services/java.sql.Driver
设置中查找并注册驱动程序。当您调用 getConnection()
时,DriverManager 将为给定的 jdbc URL 选择第一个合适的驱动程序。
现在 redshift 和 postgres 驱动程序在 jdbc url 方面有所不同,但是(引用自 redshift 文档 http://docs.aws.amazon.com/redshift/latest/mgmt/configure-jdbc-connection.html#obtain-jdbc-url):
A JDBC URL specified with the former format of jdbc:postgresql://endpoint:port/database will still work.
现在,发生的事情是,来自 redshift 的 JDBC 驱动程序被加载到服务条目上,并将自己定位为 redshift jdbc URL 和 postgres 的驱动程序一.
我无法准确判断 DriverManager
是否允许通过加载另一个驱动程序来覆盖现有的 jdbc-驱动程序链接,但是解决您的问题的方法可能是显式控制加载 postgres 驱动程序首先(如果 URL 将只注册一次)或在 redshift 驱动程序之后显式加载它(如果 JDBC URL 映射可以被覆盖)。
我不知道是否有 属性 禁止 redshift 驱动程序注册 postgres URL。
我能够让驱动程序以正确的顺序持续加载的唯一方法是添加:
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;
}
}
}
这很恶心,但是很管用。
如下评论PGDatasource也使用DriverManger,但可以直接创建org.postgresql.jdbc4.Jdbc4Connection
:
Properties info = new Properties();
info.put("password", "postgres");
Jdbc4Connection c = new Jdbc4Connection(new HostSpec[]{new HostSpec("localhost", 5432)}, "postgres", "postgres", info, "");
我有一个方法可以将记录插入 Postgres 数据库,returns 为该记录生成身份字段。问题是,如果我在我的 POM 文件中包含 Redshift 驱动程序,将使用该驱动程序而不是 Postgres 驱动程序 - 并且 Redshift 驱动程序不允许返回标识值。
密码是:
try {
Class.forName( "org.postgresql.Driver" ).newInstance();
Connection connection = DriverManager.getConnection( "jdbc:postgresql://localhost:5433/postgres", "postgres", "password" );
Statement stmt = connection.createStatement();
stmt.execute( "insert into public.job ( job_name ) values ( 'test' )" , Statement.RETURN_GENERATED_KEYS );
ResultSet keyset = stmt.getGeneratedKeys();
if ( keyset.next() ) System.out.println( keyset.getLong( 1 ) );
}
catch ( Exception e ) {
e.printStackTrace();
}
当使用这个 POM 时,它有效:
<dependencies>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>9.4-1201-jdbc41</version>
</dependency>
</dependencies>
当使用这个 POM 时,它不起作用:
<dependencies>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>redshift.jdbc</artifactId>
<version>1.1.2.0002</version>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>9.4-1201-jdbc41</version>
</dependency>
</dependencies>
是什么让 Java 选择 Redshift 驱动程序而不是 Postgres 驱动程序?
(Redshift驱动的类路径是com.amazon.jdbc41.Driver,所以我认为不是类路径冲突)
TIA
你的问题是,Java 支持 JDBC 4.0 的 ServiceLoader
机制。
在 JDBC 4 中,DriverManager
将从他们的 jar 文件中的 META-INF/services/java.sql.Driver
设置中查找并注册驱动程序。当您调用 getConnection()
时,DriverManager 将为给定的 jdbc URL 选择第一个合适的驱动程序。
现在 redshift 和 postgres 驱动程序在 jdbc url 方面有所不同,但是(引用自 redshift 文档 http://docs.aws.amazon.com/redshift/latest/mgmt/configure-jdbc-connection.html#obtain-jdbc-url):
A JDBC URL specified with the former format of jdbc:postgresql://endpoint:port/database will still work.
现在,发生的事情是,来自 redshift 的 JDBC 驱动程序被加载到服务条目上,并将自己定位为 redshift jdbc URL 和 postgres 的驱动程序一.
我无法准确判断 DriverManager
是否允许通过加载另一个驱动程序来覆盖现有的 jdbc-驱动程序链接,但是解决您的问题的方法可能是显式控制加载 postgres 驱动程序首先(如果 URL 将只注册一次)或在 redshift 驱动程序之后显式加载它(如果 JDBC URL 映射可以被覆盖)。
我不知道是否有 属性 禁止 redshift 驱动程序注册 postgres URL。
我能够让驱动程序以正确的顺序持续加载的唯一方法是添加:
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;
}
}
}
这很恶心,但是很管用。
如下评论PGDatasource也使用DriverManger,但可以直接创建org.postgresql.jdbc4.Jdbc4Connection
:
Properties info = new Properties();
info.put("password", "postgres");
Jdbc4Connection c = new Jdbc4Connection(new HostSpec[]{new HostSpec("localhost", 5432)}, "postgres", "postgres", info, "");