本文将介绍如何在 Java 应用中使用 JDBC 连接 PolarDB PostgreSQL 版(兼容 Oracle) 数据库。
前提条件
背景信息
JDBC(Java Database Connectivity)为 Java 应用程序提供了访问数据库的编程接口。 PolarDB PostgreSQL 版(兼容 Oracle) 数据库的 JDBC 是基于开源的 PostgreSQL JDBC 开发而来,使用 PostgreSQL 本地网络协议进行通信,允许 Java 程序使用标准的、独立于数据库的 Java 代码连接数据库。
下载驱动
|
JDK 版本 |
软件包 |
|
1.6 |
|
|
1.7 |
|
|
1.8 |
由于安全原因,兼容 Oracle 语法兼容 1.0 版本的驱动已经下线。如需帮助, 请加入相关 群组 进行处理 。
Maven 配置
目前 PolarDB 的 JDBC 驱动尚未在公开的 Maven 仓库中提供,当前仅支持通过上传 JAR 包的方式进行配置。
功能介绍
连接级参数功能
以下功能都通过一个连接参数配置,支持的参数如下表所示。所有的新增参数的生效范围都控制为连接级别,随 Connection 的生命周期生效。
|
参数名 |
说明 |
|
|
开启或关闭参数形式的自动提交。取值如下:
|
|
|
是否允许自动提交下继续调用 commit/rollback 方法。取值如下:
|
|
|
是否支持 Oracle 兼容的 BLOB。取值如下:
|
|
|
是否支持 Oracle 兼容的 CLOB。取值如下:
|
|
|
是否收集告警(防止内存溢出)。取值如下:
|
|
|
配合
|
|
|
小数长度。 |
|
|
是否支持将 Date 类型转为 Timestamp。取值如下:
|
|
|
是否支持通过
|
|
|
是否返回列名、表名的大写。取值如下:
|
|
|
是否支持将
|
|
|
支持 Oracle 语义的布尔值表示方式。取值如下:
|
数据类型解析
-
Date 类型:64 位 Date 类型的支持。
内核支持了 64 位的 Date,数据表示格式与 Oracle 相同,带有时分秒信息,对应驱动可以以 Timestamp 的方式去处理该 Date。将所有的 Date 类型(Types.DATE 或者 DATEOID)映射成 Timestamp 类型,驱动将 Date 视为 Timestamp 进行处理。
-
Interval 类型:支持 Oracle 模式的 Interval 输入。
PG 社区的驱动不支持例如
+12 12:03:12.111形式的 Interval 输入,由于目前 Oracle 模式下该形式是标准输出,所以 PolarDB PostgreSQL 版(兼容 Oracle) 支持这种形式的输出。 -
Number 类型:支持 Number 的 GET 行为。
Java.sql 的标准实现中没有 getNumber 相关的函数,只有 getInt 等函数。如果一个函数的参数类型是 Number,允许使用 getInt、setInt、RegisterParam 等接口将参数以 Int 形式传递。
-
Blob 类型:Blob 处理为 Bytea,Clob 处理为 Text。
针对 Java.sql.Blob 和 Java.sql.Clob 接口的实现。内核已经为 Blob、Clob 添加了映射,在 Java 层面也可以按照 Bytea、Text 的方式去处理。主类实现了 getBytes、setBytes、position、getBinaryStream 等方法。
-
Boolean 类型:支持布尔类型转义为 1/0。
为了保证老版本的兼容性,
setBoolean接口方法在设置时默认采用true/false。然而,用户可以通过激活boolAsInt参数来切换至与 Oracle 兼容的1/0语义,以此适应 Oracle 兼容的数据库交互需求。说明针对数字类型转换为 Boolean 类型,不同版本的驱动包处理规则存在差异,具体区别如下:
-
42.5.4.0.10 及以下版本:1 或等同于 1 的数字视为 True,0 或等同于 0 的数字视为 False,其他数字值则返回错误。
-
42.5.4.0.11 及以上版本:0 或等同于 0 的数字视为 False,其他非 0 的数字值均视为 True,此版本驱动的这一行为与 Oracle 驱动保持一致。
-
PL/SQL 适配
-
支持不带$$符号的存储过程。
支持在创建
FUNCTION/PROCEDURE等过程时省略$$符号,并支持在语法解析时截断/字符。 -
支持冒号变量名作为参数。
支持使用
:xxx这种方式传递参数,其中xxx为冒号开头的变量名。 -
支持匿名块绑定参数。
-
支持屏蔽 PLSQL 的警告信息。
防止循环中存储过多的警告信息导致内存超限。
示例
加载 JDBC 驱动
在应用中执行以下命令加载 JDBC 驱动:
Class.forName("com.aliyun.polardb2.Driver");
如果是通过项目导入的方式导入 JDBC,以上驱动都会自动注册完成,不需要额外注册。
连接数据库
jdbc:polardb 协议
在 JDBC 中,一个数据库通常用一个 URL 来表示,示例如下:
jdbc:polardb://pc-***.o.polardb.rds.aliyuncs.com:1521/polardb_test?user=test&password=Pw123456
|
参数 |
示例 |
说明 |
|
URL 前缀 |
jdbc:polardb:// |
连接
PolarDB
的
URL,使用
|
|
连接地址 |
pc-***.o.polardb.rds.aliyuncs.com |
PolarDB 集群的连接地址,如何查看连接地址请参见 查看或申请连接地址 。 |
|
端口 |
1521 |
PolarDB 集群的端口,默认为 1521。 |
|
数据库 |
polardb_test |
需要连接的数据库名。 |
|
用户名 |
test |
PolarDB 集群的用户名。 |
|
密码 |
Pw123456 |
PolarDB 集群用户名对应的密码。 |
jdbc:postgresql 协议
支持使用
jdbc:postgresql://
协议连接集群。然而,为避免与原生
PostgreSQL
驱动产生冲突而导致其他连接异常,需在连接字符串末尾添加
forceDriverType=true
参数以显式启用。使用示例如下:
jdbc:postgresql://pc-***.o.polardb.rds.aliyuncs.com:1521/postgres?forceDriverType=True
|
参数 |
示例 |
说明 |
|
URL 前缀 |
jdbc:postgresql:// |
连接
PolarDB
的
URL,使用
|
|
连接地址 |
pc-***.o.polardb.rds.aliyuncs.com |
PolarDB 集群的连接地址,如何查看连接地址请参见 查看或申请连接地址 。 |
|
端口 |
1521 |
PolarDB 集群的端口,默认为 1521。 |
查询并处理结果
访问数据库执行查询时,需要创建一个
Statement
、
PreparedStatment
或者
CallableStatement
对象。
PreparedStatment
示例如下:
PreparedStatement st = conn.prepareStatement("select id, name from foo where id > ?");
st.setInt(1, 10);
resultSet = st.executeQuery();
while (resultSet.next()) {
System.out.println("id:" + resultSet.getInt(1));
System.out.println("name:" + resultSet.getString(2));
}
调用函数/存储过程
您可使用
JDBC
的
CallableStatement
对象调用函数(Function)和存储过程(Procedure)。
PolarDB PostgreSQL 版(兼容 Oracle) 升级 CALL 函数的语法逻辑,支持更加丰富的 JDBC 绑定参数用法。使用前,请确保您使用的是最新版的 JDBC 驱动包。
参数说明
|
参数类型 |
JDBC 注册方式 |
Java 设置方式 |
Java 获取方式 |
|
|
无需注册 |
|
不可获取 |
|
|
|
|
|
|
|
|
无需设置 |
|
存储过程调用示例
集群中创建一个
test_in_out_procedure
存储过程。
CREATE OR REPLACE PROCEDURE test_in_out_procedure (a IN number, b IN OUT number, c OUT number) IS
BEGIN
b := a + b;
c := b + 1;
END;
Java
中创建一个
CallableStatement
对象,用于调用
test_in_out_procedure
存储过程。
CallableStatement cstmt = connection.prepareCall("{call test_in_out_procedure(?, ?, ?)}");
// IN 参数 a
cstmt.setInt(1, 1);
// IN OUT 参数 b
cstmt.setInt(2, 2);
cstmt.registerOutParameter(2, Types.INTEGER);
// OUT 参数 c
cstmt.registerOutParameter(3, Types.INTEGER);
cstmt.execute();
int b = cstmt.getInt(2);
int c = cstmt.getInt(3);
函数调用示例
集群中创建一个
test_in_out_function
函数。
CREATE OR REPLACE FUNCTION test_in_out_function (a IN number, b IN OUT number, c OUT number) RETURN number AS
BEGIN
b := a + b;
c := b + 1;
RETURN c + 1;
END;
在 Java 中支持两种调用方式。
-
使用 JDBC 转义语法 (Escape Syntax):
CallableStatement cstmt = connection.prepareCall("{?= call test_in_out_function(?, ?, ?)}"); // 返回值 r cstmt.registerOutParameter(1, Types.INTEGER); // IN 参数 a cstmt.setInt(2, 1); // IN OUT 参数 b cstmt.setInt(3, 2); cstmt.registerOutParameter(3, Types.INTEGER); // OUT 参数 c cstmt.registerOutParameter(4, Types.INTEGER); cstmt.execute(); int r = cstmt.getInt(1); int b = cstmt.getInt(3); int c = cstmt.getInt(4); -
使用
BEGIN ... END;匿名块包装:CallableStatement cstmt = connection.prepareCall("BEGIN ? := test_in_out_function(?, ?, ?); END;"); // 返回值 r cstmt.registerOutParameter(1, Types.INTEGER); // IN 参数 a cstmt.setInt(2, 1); // IN OUT 参数 b cstmt.setInt(3, 2); cstmt.registerOutParameter(3, Types.INTEGER); // OUT 参数 c cstmt.registerOutParameter(4, Types.INTEGER); cstmt.execute();
函数作为存储过程调用
集群中创建一个
test_in_out_function_as_procedure_1
存储过程。其中,直接调用
test_in_out_function
函数并给
OUT
参数赋值。
CREATE OR REPLACE PROCEDURE test_in_out_function_as_procedure_1 (
a IN number,
b IN OUT number,
c OUT number,
r OUT number
BEGIN
r := test_in_out_function(a, b, c);
END;
CallableStatement cstmt = connection.prepareCall("{call test_in_out_function_as_procedure_1(?, ?, ?, ?)}");
cstmt.setInt(1, 1); // a
cstmt.setInt(2, 2); // b
cstmt.registerOutParameter(2, Types.INTEGER);
cstmt.registerOutParameter(3, Types.INTEGER); // c
cstmt.registerOutParameter(4, Types.INTEGER); // r
cstmt.execute();
int b = cstmt.getInt(2);
int c = cstmt.getInt(3);
int r = cstmt.getInt(4);
函数通过 SELECT INTO 调用
集群中创建一个
test_in_out_function_as_procedure_2
存储过程。其中,
test_in_out_function
函数通过
SELECT INTO
调用。
CREATE OR REPLACE PROCEDURE test_in_out_function_as_procedure_2 (
a IN number,
b IN OUT number,
c OUT number,
r OUT number
BEGIN
SELECT test_in_out_function(a, b, c) INTO r FROM dual;
END;
CallableStatement cstmt = connection.prepareCall("{call test_in_out_function_as_procedure_2(?, ?, ?, ?)}");
cstmt.setInt(1, 1); // a
cstmt.setInt(2, 2); // b
cstmt.registerOutParameter(2, Types.INTEGER);
cstmt.registerOutParameter(3, Types.INTEGER); // c
cstmt.registerOutParameter(4, Types.INTEGER); // r
cstmt.execute();
使用结构体(Struct)作为参数
42.5.4.0.12(2025-08-13)
版本后,数据库驱动支持
createStruct
语法,您可使用
Struct
结构体作为函数的入参。这使得在
Java
代码中构建并传递数据库自定义的复合类型(或对象类型)变得非常方便。
// 假设 conn 是一个已建立的数据库连接对象
public void testSelectBoolean1() throws Exception {
// 1. 准备构成结构体的属性数组。
// 数组元素的顺序和类型必须与数据库中定义的 test_object 类型严格匹配。
Object[] addressAttributes = new Object[] {
Integer.valueOf(42), // Integer
new BigDecimal("9999.99"), // java.math.BigDecimal
Boolean.TRUE, // Boolean
new Date(), // java.util.Date
new Timestamp(System.currentTimeMillis()), // java.sql.Timestamp
"这是一个测试字符串", // String
new StringBuilder("可变字符串"), // StringBuilder
null, // null
// 2. 使用 conn.createStruct 创建 Struct 对象
Struct addressStruct = conn.createStruct("test_object", addressAttributes);
// 3. 准备并执行 CallableStatement 来调用函数
CallableStatement stmt = conn.prepareCall("{? = call test_object_func(?)}");
stmt.registerOutParameter(1, Types.VARCHAR);
stmt.setObject(2, addressStruct);
stmt.execute();
// 4. 获取并打印函数返回值
System.out.println(stmt.getObject(1).toString());
stmt.close();
}
相关工具适配
适配 Hibernate
-
hibernate.cfg.xml驱动类与方言配置:如果您的工程使用 Hibernate 连接数据库,请在您的 Hibernate 配置文件hibernate.cfg.xml中配置 PolarDB 数据库的驱动类和方言。说明Hibernate 需要为 3.6 及以上版本才支持 PostgresPlusDialect 方言。
<property name="connection.driver_class">com.aliyun.polardb2.Driver</property> <property name="connection.url">jdbc:polardb://pc-***.o.polardb.rds.aliyuncs.com:1521/polardb_test</property> <property name="dialect">org.hibernate.dialect.PostgresPlusDialect</property> -
DATE类型配置:对于表中DATE类型的列,需要在 Hibernate 的.hbm.xml文件中调整配置type="java.util.Date"以确保 Hibernate 读写 PolarDB 的DATE类型数据时保留时分秒精度。如果直接使用type="date",则可能造成DATE精度丢失。示例配置如下:<!-- 其他配置信息 --> <hibernate-mapping package="com.aliyun.polardb2.demo"> <class name="TestTableEntity" table="test_table_name"> <!-- 其他列信息 --> <property name="currentDate" column="curr_date" type="java.util.Date"/> <!-- 指定java.util.Date类型以保留date精度 --> <!-- 其他列信息 --> </class> </hibernate-mapping> -
LOB(Large Objects)大对象类型配置:原生 PostgreSQL 不支持 LOB 类型,因此 PostgresPlusDialect 方言将 CLOB、BLOB 类型的列都映射为
oid类型的列,这会导致插入该列的字符串被转为oid数字。 PolarDB PostgreSQL 版 Oracle 语法兼容 2.0 将 CLOB 类型映射为text类型,BLOB 映射为bytea类型。需要指定列的类型使之生效,以下配置二选一即可。-
配置一:Java 类定义。
@Lob @Column(name = "col_clob") @Type(type = "text") private String columnClob; @Column(name = "col_blob") @Type(type = "bytea") private String columnBlob; -
配置二:在 Hibernate 的
.hbm.xml文件定义。<!-- 其他配置信息 --> <hibernate-mapping package="com.aliyun.polardb2.demo"> <class name="TestTableEntity" table="test_table_name"> <!-- 其他列信息 --> <property name="columnClob" column="col_clob" type="text"/> <property name="columnBlob" column="col_blob" type="bytea"/> <!-- 其他列信息 --> </class> </hibernate-mapping>
-
Druid 连接池
Druid 是一个数据库连接池,您可以通过它来管理应用程序与 PolarDB PostgreSQL 版(兼容 Oracle) 之间的连接。当您使用 Druid 连接时,为确保功能的完整性和稳定性,请注意以下关键配置:
-
Druid 是从
1.2.26版本开始支持 PolarDB 的,请确保您项目中引用的版本不低于此版本。以 Maven 为例:<dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.26</version> </dependency> -
在初始化连接池时,必须显式设置驱动类名(
driverClassName)和数据库类型(dbType)。DruidDataSource dataSource = new DruidDataSource(); dataSource.setDriverClassName("com.aliyun.polardb2.Driver"); dataSource.setDbType("polardb2"); -
使用 SQL 防火墙(
WallFilter)严格检查业务 SQL 是否符合 Oracle 语法规范,防止 SQL 注入风险。// 1. 配置 WallConfig WallConfig wallConfig = new WallConfig(); // 1.1 是否进行严格的语法检查,必填 wallConfig.setStrictSyntaxCheck(true); // 1.2 更精细的语法控制,仅列举部分,可选 wallConfig.setMultiStatementAllow(false); // 是否允许一次执行多条语句 wallConfig.setCommentAllow(true); // 允许注释 wallConfig.setSelectIntoAllow(true); // 允许SELECT INTO wallConfig.setDeleteWhereNoneCheck(true); // 检查DELETE语句是否无条件 // 2. 使用 WallConfig 配置 WallFilter WallFilter wallFilter = new WallFilter(); wallFilter.setConfig(wallConfig); // 3. 使用 WallFilter 配置连接池 DruidDataSource dataSource = new DruidDataSource(); dataSource.getProxyFilters().add(wallFilter); -
如果需要在 Druid 连接池中对数据库密码进行加密,请参见 数据库密码加密 。
适配 WebSphere
使用 WebSphere 时,配置 PolarDB 的 JDBC 作为数据源,步骤如下所示:
-
数据库类型选择 用户自定义的 。
-
实现类名为:
com.aliyun.polardb2.ds.PGConnectionPoolDataSource。 -
类路径选择 JDBC jar 包所在路径。
适配 Spring 框架
在
Spring
框架中使用新版本
JDBC(版本号≥ 42.5.4.0.11)时,可以直接将结构体类型(Struct)作为存储过程参数传入,无需额外的代码改造。以下示例演示了如何通过
GetUserProcedure
方法调用存储过程
get_user_info
,其中参数
c
为复合类型
com
,通过构建相应的结构体对象实现复合类型的参数传递。
public class GetUserProcedure extends StoredProcedure {
private static final String PROCEDURE_NAME = "get_user_info";
public GetUserProcedure(DataSource dataSource) {
super(dataSource, PROCEDURE_NAME);
init();
private void init() {
// 声明输入参数
declareParameter(new SqlParameter("p_user_id", Types.NUMERIC));
declareParameter(new SqlParameter("c", Types.STRUCT, "com"));
compile(); // 必须调用 compile()
public Map<String, Object> getUserInfo(Integer userId) {
Map<String, Object> inputs = new HashMap<>();
inputs.put("p_user_id", userId);
Calendar cal = Calendar.getInstance();
cal.set(2023, Calendar.OCTOBER, 1, 12, 30, 45); // 注意:Calendar 的月份从 0 开始
cal.set(Calendar.MILLISECOND, 0);
Rec rec = new Rec();
rec.t1 = 1;
rec.t2 = "some text";
rec.t3 = new Date(cal.getTime().getTime());
rec.t4 = true;
rec.t5 = null;
inputs.put("c", rec);
return execute(inputs); // 执行存储过程
}
适配 Apache ShardingSphere
您可以通过 Apache ShardingSphere 连接并管理 PolarDB PostgreSQL 版(兼容 Oracle) 集群,以实现数据分片、读写分离等高级功能。由于 PolarDB 完全兼容 PostgreSQL 协议,而 ShardingSphere 原生支持该协议,因此两者可以无缝集成。
注意事项
配置时,请遵循以下关键步骤,确保 ShardingSphere 能正确识别并使用 PolarDB 的 JDBC 驱动。
-
ShardingSphere 配置 :在 ShardingSphere 的数据源配置中,需将驱动类名(
driverClassName)设置为com.aliyun.polardb2.Driver。 -
驱动版本 :需为 42.5.4.0.12(2025-08-13) 及以上版本。
-
连接协议 :需使用 jdbc:postgresql 协议 ,且需在连接字符串末尾添加
forceDriverType=true参数。
连接示例
以下是一个在
ShardingSphere
中配置
PolarDB
数据源的
YAML
示例(
config-sharding.yaml
):
dataSources:
dataSourceClassName: com.zaxxer.hikari.HikariDataSource
driverClassName: com.aliyun.polardb2.Driver
jdbcUrl: jdbc:postgresql://pc-***.o.polardb.rds.aliyuncs.com:1521/postgres?forceDriverType=True
username: ******
password: ******
maxPoolSize: 2
minPoolSize: 2
dataSourceClassName: com.zaxxer.hikari.HikariDataSource
driverClassName: com.aliyun.polardb2.Driver
jdbcUrl: jdbc:postgresql://pc-***.o.polardb.rds.aliyuncs.com:1521/postgres?forceDriverType=True
username: ******
password: ******
maxPoolSize: 2
minPoolSize: 2
常见问题
如何选择 JDBC 驱动,是否可以使用开源社区驱动?
PolarDB PostgreSQL 版(兼容 Oracle) 兼容版在开源 PostgreSQL 的基础上实现了众多兼容性相关的特性,有些特性需要驱动层配合实现,因此,推荐使用 PolarDB 的 JDBC 驱动。相关驱动可以在官网驱动下载页面下载。
公共 Maven 仓库是否有 PolarDB JDBC 驱动?
按照官网描述,JDBC 驱动需要在官网下载 jar 包,对于 Maven 工程需要手动安装该 jar 包至本地仓库使用,目前仅支持官网下载 JDBC 驱动包一种方式。
如何查看版本号?
通过运行
java -jar 驱动名
来查看版本号。
是否支持在 URL 中配置多个 IP 和端口?
PolarDB PostgreSQL 版(兼容 Oracle) 的 JDBC 驱动支持在 URL 中配置多个 IP 和端口,示例如下:
jdbc:polardb://1.2.XX.XX:5432,2.3.XX.XX:5432/postgres
配置多个 IP 后,创建连接时会依次尝试通过这些 IP 创建连接,若都不能创建连接,则连接创建失败。每个 IP 尝试创建连接的超时时间默认为 10s,即 connectTimeout,若要修改超时时间,可在连接串中添加该参数进行设置。
游标类型如何选择?
如果是 java 1.8 之前的 JDK,使用 Types.REF;如果是 java 1.8 及其之后的版本,可以使用 Types.REF_CURSOR。
是否支持默认返回大写的列名?
可以在
JDBC
连接串中添加参数
oracleCase=true
,该参数会将返回的列名默认转换为大写,示例如下:
jdbc:polardb://1.2.XX.XX:5432,2.3.XX.XX:5432/postgres?oracleCase=true
版本更新日志
42.5.4.0.12(2025-08-13)
-
支持 使用结构体(Struct)作为参数 。
-
支持使用 jdbc:postgresql 协议 连接集群。
42.5.4.0.10.11(2025-07-10)
-
支持通过 CallableStatement 接口 读写函数以及存储过程 ,支持各种类型的 IN&OUT&INOUT 参数。
-
支持 SQLCODE 错误码字段 ,与数据库内核的错误处理机制兼容。
-
支持 Spring 框架 中使用 Types.STRUCT 结构体。
-
优化 数字类型到布尔值的转换规则 。
-
增强类型绑定支持。
42.5.4.0.10.9(2025-03-19)
-
支持 Oracle 风格的函数绑定参数功能。
-
修复一个 END 会导致解析失败的缺陷。
42.5.4.0.10.7(2025-01-06)
-
支持兼容 Oracle 方式的注释功能(即支持
/* /* Comments */功能)。 -
修复 Mybatis 调用 Clob 接口时,使用空值导致的
Misuse of castNonNull问题。
42.5.4.0.10.6(2024-12-04)
-
支持高版本 JDBC 的
Channel Binding功能。 -
升级
escapeSyntaxCallMode参数默认值为callIfNoReturn,适配 Oracle 的参数绑定行为。 -
修复
attidentity识别错误可能导致的列类型获取不正确缺陷。
42.5.4.0.10.5(2024-10-24)
-
优化
resetNlsFormat参数的设置,确保连接时的正确配置。同时,避免在审计日志中留下非预期的执行记录。 -
修复逻辑复制测试中因无法识别
java.nio.Buffer类型接口而导致的错误。 -
修复存储过程中
CASE WHEN...END识别结束解析不正确的问题。
42.5.4.0.10.4 (2024-09-02)
-
修复了 PL 块中绑定不正确的问题。针对此问题对性能的影响,默认已关闭该功能。
-
支持在同一类型内部进行隐式转换,允许字符类型(如
VARCHAR、CHAR)和数字类型(如NUMERIC、INTEGER、DOUBLE)作为INOUT参数相互转换。 -
驱动中元信息的
getDatabaseProductName()函数返回值现为:“POLARDB2 Database Compatible with Oracle”。
42.5.4.0.10.2 (2024-07-19)
-
修复了在
Mybatis中,当对象实体注册类型为Timestamp时,数据库无法正确推断参数类型的问题。
专家面对面
关于 JDBC,如果您在使用过程中有任何问题,请进钉钉群咨询。 钉钉 群号: 71365019522。