本文将介绍如何在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时,数据库无法正确推断参数类型的问题。