本文将介绍如何在Java应用中使用JDBC连接PolarDB PostgreSQL版(兼容Oracle)数据库。

前提条件

  • 已经在PolarDB集群创建用户,如何创建用户请参见创建数据库账号
  • 已经将需要访问PolarDB集群的主机IP地址添加到白名单,如何添加白名单请参见设置集群白名单

背景信息

JDBC(Java Database Connectivity)为Java应用程序提供了访问数据库的编程接口。PolarDB PostgreSQL版(兼容Oracle)数据库的JDBC是基于开源的PostgreSQL JDBC开发而来,使用PostgreSQL本地网络协议进行通信,允许Java程序使用标准的、独立于数据库的Java代码连接数据库。

JDBC驱动程序使用了PostgreSQL 3.0协议,与Java 6(JDBC 4.0)、Java 7(JDBC4.1)和Java 8(JDBC4.2)兼容。

下载JDBC

JDBC驱动

阿里云提供了兼容Java 6、Java 7和Java 8三个Java版本的JDBC驱动,对应三个Jar包,包名分别为polardb-jdbc16.jarpolardb-jdbc17.jarpolardb-jdbc18.jar 。您可根据应用使用的JDK版本选择合适的JDBC。

配置JDBC

在Java应用中使用JDBC前,需要将JDBC驱动包的路径添加至CLASSPATH中。例如您的JDBC驱动放置的路径为/usr/local/polardb/share/java/,在CLASSPATH中添加JDBC驱动路径的命令如下:

export CLASSPATH=$CLASSPATH:/usr/local/polardb/share/java/<安装的Jar包名.jar>

示例:

export CLASSPATH=$CLASSPATH:/usr/local/polardb/share/java/polardb-jdbc18.jar

您可以通过如下命令查看当前使用的JDBC版本:

#java -jar <安装的Jar包名.jar>

示例:

#java -jar polardb-jdbc18.jar
POLARDB JDBC Driver 42.2.XX.XX.0

访问PolarDB

  • 示例
    package com.aliyun.polardb;
    
    import java.sql.Connection;
    import java.sql.Driver;
    import java.sql.DriverManager;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.sql.Statement;
    import java.util.Properties;
    
    /**
     * POLARDB JDBC DEMO
     * <p>
     * Please make sure the host ip running this demo is in you cluster's white list.
     */
    public class PolarDBJdbcDemo {
      /**
       * Replace the following information.
       */
      private final String host = "***.o.polardb.rds.aliyuncs.com";
      private final String user = "***";
      private final String password = "***";
      private final String port = "1521";
      private final String database = "db_name";
    
      public void run() throws Exception {
        Connection connect = null;
        Statement statement = null;
        ResultSet resultSet = null;
    
        try {
          Class.forName("com.aliyun.polardb.Driver");
    
          Properties props = new Properties();
          props.put("user", user);
          props.put("password", password);
          String url = "jdbc:polardb://" + host + ":" + port + "/" + database;
          connect = DriverManager.getConnection(url, props);
    
          /**
           * create table foo(id int, name varchar(20));
           */
          String sql = "select id, name from foo";
          statement = connect.createStatement();
          resultSet = statement.executeQuery(sql);
          while (resultSet.next()) {
            System.out.println("id:" + resultSet.getInt(1));
            System.out.println("name:" + resultSet.getString(2));
          }
        } catch (Exception e) {
          e.printStackTrace();
          throw e;
        } finally {
          try {
            if (resultSet != null)
              resultSet.close();
            if (statement != null)
              statement.close();
            if (connect != null)
              connect.close();
          } catch (SQLException e) {
            e.printStackTrace();
            throw e;
          }
        }
      }
    
      public static void main(String[] args) throws Exception {
        PolarDBJdbcDemo demo = new PolarDBJdbcDemo();
        demo.run();
      }
    }
  • 加载JDBC驱动
    在应用中执行以下命令加载 JDBC 驱动:
    Class.forName("com.aliyun.polardb.Driver");
  • 连接数据库

    在JDBC中,一个数据库通常用一个URL来表示,示例如下。

    jdbc:polardb://pc-***.o.polardb.rds.aliyuncs.com:1521/polardb_test?user=test&password=Pw123456
    参数示例说明
    URL前缀jdbc:polardb://连接PolarDB的URL统一使用jdbc:polardb://作为前缀。
    连接地址pc-***.o.polardb.rds.aliyuncs.comPolarDB集群的连接地址,如何查看连接地址请参见查看或申请连接地址
    端口1521PolarDB集群的端口,默认为1521。
    数据库polardb_test需要连接的数据库名。
    用户名testPolarDB集群的用户名。
    密码Pw123456PolarDB集群用户名对应的密码。
  • 查询并处理结果

    访问数据库执行查询时,需要创建一个StatementPreparedStatment或者CallableStatement对象。

    上述示例中使用了Statement,使用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));
    }

    CallableStatement用于处理存储过程,示例如下:

    String sql = "{?=call getName (?, ?, ?)}";
    CallableStatement stmt = conn.prepareCall(sql);
    stmt.registerOutParameter(1, java.sql.Types.INTEGER);
    
    //Bind IN parameter first, then bind OUT parameter
    int id = 100;
    stmt.setInt(2, id); // This would set ID as 102
    stmt.registerOutParameter(3, java.sql.Types.VARCHAR);
    stmt.registerOutParameter(4, java.sql.Types.INTEGER);
    
    //Use execute method to run stored procedure.
    stmt.execute();
    
    //Retrieve name with getXXX method
    String name = stmt.getString(3);
    Integer msgId = stmt.getInt(4);
    Integer result = stmt.getInt(1);
    System.out.println("Name with ID:" + id + " is " + name + ", and messegeID is " + msgId + ", and return is " + result);

    以上代码使用的存储过程getName如下:

    CREATE OR REPLACE FUNCTION getName(
        id        In      Integer,
        name      Out     Varchar2,
        result    Out     Integer
      ) Return Integer
    Is
      ret     Int;
    Begin
      ret := 0;
      name := 'Test';
      result := 1;
      Return(ret);
    End;
    说明 当存储过程为游标类型时,不同的Java版本对应的游标类型不同:
    • Java8及以后的版本使用Types.REF_CURSOR类型游标。
    • Java8之前的版本使用Types.REF类型游标。
  • 设置FetchSize
    默认情况下,驱动会一次性从数据库端获取所有数据,对于数据量很大的查询,这会占用客户端大量内存,甚至造成 OOM,为避免此类情况,JDBC提供了基于游标的ResultSet,批量获取数据集。使用方法如下:
    • 设置FetchSizeFetchSize默认为0,即获取所有数据。
    • 设置连接的autoCommitfalse
    // make sure autocommit is off
    conn.setAutoCommit(false);
    Statement st = conn.createStatement();
    
    // Set fetchSize to use cursor
    st.setFetchSize(50);
    ResultSet rs = st.executeQuery("SELECT * FROM mytable");
    while (rs.next())
    {
        System.out.print("a row was returned.");
    }
    rs.close();
    
    // Reset fetchSize to turn off the cursor
    st.setFetchSize(0);
    rs = st.executeQuery("SELECT * FROM mytable");
    while (rs.next())
    {
        System.out.print("many rows were returned.");
    }
    rs.close();
    
    // Close the statement.
    st.close();

Maven工程

如果您的Java项目使用Maven构建,可以通过如下命令将PolarDB的JDBC驱动包安装至您的本地仓库:

 mvn install:install-file -DgroupId=com.aliyun -DartifactId=<安装的Jar包名> -Dversion=1.1.2 -Dpackaging=jar -Dfile=/usr/local/polardb/share/java/<安装的Jar包名.jar>

示例:

 mvn install:install-file -DgroupId=com.aliyun -DartifactId=polardb-jdbc18 -Dversion=1.1.2 -Dpackaging=jar -Dfile=/usr/local/polardb/share/java/polardb-jdbc18.jar

在Maven工程的pom.xml文件中添加如下依赖。

<dependency>
    <groupId>com.aliyun</groupId>
    <artifactId><安装的Jar包名></artifactId>
    <version>1.1.2</version>
</dependency>

示例:

<dependency>
    <groupId>com.aliyun</groupId>
    <artifactId>polardb-jdbc18</artifactId>
    <version>1.1.2</version>
</dependency>

Hibernate

如果您的工程使用Hibernate连接数据库,请在您的Hibernate配置文件hibernate.cfg.xml中配置PolarDB数据库的驱动类和方言。

说明 Hibernate需要为3.6及以上版本才支持PostgresPlusDialect方言。
<property name="connection.driver_class">com.aliyun.polardb.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>

Druid连接池

  • Druid 1.1.24及其之后的版本默认支持PolarDB的驱动,无需设置driver namedbtype参数。
  • Druid 1.1.24之前的版本,需要显式设置driver namedbtype参数,如下所示:
    dataSource.setDriverClassName("com.aliyun.polardb.Driver");
    dataSource.setDbType("postgresql");
    说明 Druid 1.1.24之前版本没有适配PolarDB,因此dbtype需要设置为postgresql

如果您需要在Druid连接池中对数据库密码进行加密,请参见数据库密码加密

适配Activiti

如果您的应用使用了业务流程管理框架Activiti,在PolarDB数据源初始化时,可能会出现以下错误信息。

couldn't deduct database type from database product name 'POLARDB Database Compatible with Oracle'

这是因为Activiti内置了一些数据库版本信息和数据库类型的映射关系,导致无法正确映射PolarDB版本信息。您可以通过设置SpringProcessEngineConfiguration子类,在该子类中重载buildProcessEngine的方法来解决问题。在该解决方法中,您需要显式指定数据库类型,配置方法请参见以下示例。

package com.aliyun.polardb;

import org.activiti.engine.ProcessEngine;
import org.activiti.spring.SpringProcessEngineConfiguration;

public class PolarDBSpringProcessEngineConfiguration extends SpringProcessEngineConfiguration {

    public PolarDBSpringProcessEngineConfiguration() {
        super();
    }

    @Override
    public ProcessEngine buildProcessEngine() {
        setDatabaseType(DATABASE_TYPE_POSTGRES);
        return super.buildProcessEngine();
    }
}

SpringProcessEngineConfiguration子类放在您的工程中,在配置文件中设置使用该类加载配置,并初始化引擎,具体信息请参见以下示例。

<bean id="processEngineConfiguration" class="com.aliyun.polardb.PolarDBSpringProcessEngineConfiguration">
      <property name="dataSource" ref="dataSource"/>
      <property name="transactionManager" ref="transactionManager"/>
      <property name="databaseSchemaUpdate" value="true"/>
      <!-- 其他配置在此省略 -->
</bean>

适配Quartz

Quartz是一款开源的作业调度库,使用Quartz连接PolarDB时,需要将org.quartz.jobStore.driverDelegateClass配置为 org.quartz.impl.jdbcjobstore.PostgreSQLDelegate,如下所示:
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.PostgreSQLDelegate

适配WebSphere

使用 WebSphere时,配置PolarDB的JDBC作为数据源,步骤如下所示:
  1. 数据库类型选择用户自定义的
  2. 实现类名为:com.aliyun.polardb.ds.PGConnectionPoolDataSource
  3. 类路径选择JDBC jar包所在路径。

适配MyBatis

使用MyBatis时,可能需要配置databaseIdProvider,默认配置如下所示:

<databaseIdProvider type="DB_VENDOR">
  <property name="SQL Server" value="sqlserver"/>
  <property name="DB2" value="db2"/>
  <property name="Oracle" value="oracle" />
</databaseIdProvider>

databaseIdProvider的目的是提供一个数据库产品名到特定名称(即databaseId)的映射关系,即便数据库的产品名因数据库版本变化而发生改变,也可以映射到相同的名称。

在MyBatis的XML映射文件中,可以为SQL语句指定databaseId属性,说明该SQL仅能够在该databaseId对应的数据库上执行。因此,在加载XML映射文件时,MyBatis仅加载databaseId与当前数据库匹配的SQL语句,如果SQL语句没有设置databaseId属性则总是会被加载。

因此,如果XML映射文件中的SQL均没有指定databaseId,默认配置信息可以不做任何修改。如果需要通过databaseId识别特定于PolarDB执行的SQL,则可以添加如下配置信息,并在XML映射文件中使用polardb作为SQL的databaseId。
  <property name="POLARDB" value="polardb" />

常见问题

  • Q:如何选择JDBC驱动,是否可以使用开源社区驱动?

    A:PolarDB PostgreSQL版(兼容Oracle)兼容版在开源PostgreSQL的基础上实现了众多兼容性相关的特性,有些特性需要驱动层配合实现,因此,推荐使用PolarDB的JDBC驱动。相关驱动可以在官网驱动下载页面下载。

  • Q:公共Maven仓库是否有PolarDB JDBC驱动?

    A:按照官网描述,JDBC驱动需要在官网下载jar包,对于Maven工程需要手动安装该jar包至本地仓库使用,目前仅支持官网下载JDBC驱动包一种方式。

  • Q:如何查看版本号?

    A:通过运行java -jar 驱动名来查看版本号。

  • Q:是否支持在URL中配置多个IP和端口?

    A:PolarDB PostgreSQL版(兼容Oracle)的JDBC驱动支持在URL中配置多个IP和端口,示例如下:
    jdbc:poalardb://1.2.XX.XX:5432,2.3.XX.XX:5432/postgres
    说明 配置多个IP后,创建连接时会依次尝试通过这些IP创建连接,若都不能创建连接,则连接创建失败。每个IP尝试创建连接的超时时间默认为10s,即connectTimeout,若要修改超时时间,可在连接串中添加该参数进行设置。
  • Q:游标类型如何选择?

    A:如果是java 1.8之前的JDK,使用Types.REF;如果是java 1.8及其之后的版本,可以使用Types.REF_CURSOR。

  • Q:是否支持默认返回大写的列名?

    A:可以在JDBC连接串中添加参数oracleCase=true,该参数会将返回的列名默认转换为大写,示例如下:
    jdbc:poalardb://1.2.XX.XX:5432,2.3.XX.XX:5432/postgres?oracleCase=true