MaxCompute JDBC driver是MaxCompute向您提供的Java Database Connectivity (JDBC)实现,让您可以通过标准的JDBC编程接口使用MaxCompute进行海量数据的分布式计算查询。MaxCompute JDBC driver还可以用于连接MaxCompute和支持JDBC的工具。

JDBC下载

您可以在开源项目的 release 页面,或者 Maven 获取MaxCompute各版本的Jar包。我们推荐使用包含了完整依赖的jar-with-dependencies的最新版本。

通过Maven方式使用MaxCompute JDBC的样例POM(Project Object Model,项目对象模型 )片段如下。
<dependency>
  <groupId>com.aliyun.odps</groupId>
  <artifactId>odps-jdbc</artifactId>
  <version>3.0.1</version>
  <classifier>jar-with-dependencies</classifier>
</dependency>
说明 MaxCompute JDBC driver是开放源代码项目,项目地址位于 https://github.com/aliyun/aliyun-odps-jdbc

MaxCompute非常欢迎您参与到JDBC驱动的开发和改进工作。您可以在该项目的issue页面反馈问题,以及通过Pull Request的方式对源代码进行改进。issue及PullRequest请遵循开源项目的模板要求。

注意事项

  • 通过MaxCompute JDBC driver执行SQL并获取结果需要以下权限:
    • 为项目空间的成员。
    • 有项目空间中CreateInstance权限。
    • 有目标表的Select与Download权限。
    1.9及更早版本的MaxCompute JDBC driver 每个查询都会创建临时表,并通过 Tunnel 从临时表获取结果,正常使用这些版本的 JDBC 需要 CreateTable 权限。而从2.2版本开始,MaxCompute JDBC driver 不再创建临时表,直接通过Instance Tunnel获取查询结果,无此权限要求限制。更多MaxCompute权限的介绍,请参考授权
  • MaxCompute提供了数据保护功能,当数据保护模式开启时,您将无法将数据转移到项目空间之外。此时低于 2.4 的 JDBC 无法获取 result set。2.4 及更高版本则可以获得不超过READ_TABLE_MAX_ROW 定义的行数(参考项目空间操作)。更多数据保护功能的介绍,请参考项目空间的数据保护
  • MaxCompute 2.0新增了很多数据类型,如TINYINT,SMALLINT,DATETIME,TIMESTAMP,ARRAY,MAP,STRUCT等。您如果需要使用这些新类型,在执行SQL之前需要执行以下语句,打开2.0类型开关。更多MaxCompute 2.0类型的介绍,请参考数据类型
    set odps.sql.type.system.odps2=true

连接MaxCompute

使用JDBC连接MaxCompute的编程样例如下:
  1. 加载driver。
    Class.forName("com.aliyun.odps.jdbc.OdpsDriver");
  2. 通过DriverManager创建Connection。
    Connection cnct = DriverManager.getConnection(url, accessId, accessKey);
    参数说明如下:
    • url:格式为jdbc:odps:<MaxCompute endpoint>?project=<MaxCompute project name>。 其中:
      • <maxcompute_endpoint>为您所在Region的MaxCompute Endpoint。例如,华东1Region 外网Endpoint:http://service.cn-hangzhou.maxcompute.aliyun.com/api。更多关于Endpoint的配置信息,请参见配置Endpoint
      • <maxcompute_project_name> 为您的MaxCompute项目空间名称。
      举例如下。
      jdbc:odps:http://service.cn-hangzhou.maxcompute.aliyun.com/api?project=test_project
    • accessId:创建项目空间的AccessID。
    • accessKey:创建项目空间的AccessID对应的AccessKey。
  3. 执行查询。
    Statement stmt = cnct.createStatement();
    ResultSet rset = stmt.executeQuery("SELECT foo FROM bar");
    
    while (rset.next()) {
        // process the results
    }
    
    rset.close();
    stmt.close();
    conn.close();

示例代码

  • 删除表,创建表,获取metadata
    import java.sql.Connection;
    import java.sql.DatabaseMetaData;
    import java.sql.DriverManager;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.sql.Statement;
    
    public class Main {
    
        private static final String DRIVER_NAME = "com.aliyun.odps.jdbc.OdpsDriver";
    
        public static void main(String[] args) throws SQLException {
            try {
                Class.forName(DRIVER_NAME);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
                System.exit(1);
            }
    
            Connection conn = DriverManager.getConnection(
                "jdbc:odps:<maxcompute endpoint>?project=<maxcompute project>",
                "aliyun accessId", "aliyun accessKey");
    
    
            // create a table
            Statement stmt = conn.createStatement();
            final String tableName = "jdbc_test";
            stmt.execute("DROP TABLE IF EXISTS " + tableName);
            stmt.execute("CREATE TABLE " + tableName + " (key BIGINT, value STRING)");
    
            // get meta data
            DatabaseMetaData metaData = conn.getMetaData();
            System.out.println("product = " + metaData.getDatabaseProductName());
            System.out.println("jdbc version = "
                + metaData.getDriverMajorVersion() + ", "
                + metaData.getDriverMinorVersion());
            ResultSet tables = metaData.getTables(null, null, tableName, null);
            while (tables.next()) {
                String name = tables.getString("TABLE_NAME");
                System.out.println("inspecting table: " + name);
                ResultSet columns = metaData.getColumns(null, null, name, null);
                while (columns.next()) {
                    System.out.println(
                        columns.getString("COLUMN_NAME") + "\t" +
                            columns.getString("TYPE_NAME") + "(" +
                            columns.getInt("DATA_TYPE") + ")");
                }
                columns.close();
            }
    
            tables.close();
            stmt.close();
            conn.close();
        }
    }
    预期输出
    product = MaxCompute/ODPS
    jdbc version = 3, 0
    inspecting table: jdbc_test
    key    BIGINT(-5)
    value    STRING(12)
  • 执行INSERT
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.SQLException;
    import java.sql.Statement;
    
    public class Main {
    
        private static final String DRIVER_NAME = "com.aliyun.odps.jdbc.OdpsDriver";
    
        public static void main(String[] args) throws SQLException {
            try {
                Class.forName(DRIVER_NAME);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
                System.exit(1);
            }
    
            Connection conn = DriverManager.getConnection(
                "jdbc:odps:http://10.101.222.162:8002/odps_dailyrunnew?project=odps_mingyou_test",
                "63wd3dpztlmb5ocdkj94pxmm", "oRd30z7sV4hBX9aYtJgii5qnyhg=");
    
            Statement stmt = conn.createStatement();
            // The following DML also works
            //String dml = "INSERT INTO jdbc_test SELECT 1, \"foo\"";
            String dml = "INSERT INTO jdbc_test VALUES(1, \"foo\")";
            int ret = stmt.executeUpdate(dml);
    
            assert ret == 1;
    
            stmt.close();
            conn.close();
        }
    }
  • 执行批量INSERT
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.PreparedStatement;
    import java.sql.SQLException;
    
    public class Main {
    
        private static final String DRIVER_NAME = "com.aliyun.odps.jdbc.OdpsDriver";
    
        public static void main(String[] args) throws SQLException {
            try {
                Class.forName(DRIVER_NAME);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
                System.exit(1);
            }
    
            Connection conn = DriverManager.getConnection(
                "jdbc:odps:<maxcompute endpoint>?project=<maxcompute project>",
                "aliyun accessId", "aliyun accessKey");
    
            PreparedStatement pstmt = conn.prepareStatement("INSERT INTO jdbc_test VALUES(?, ?)");
    
            pstmt.setLong(1, 1L);
            pstmt.setString(2, "foo");
            pstmt.addBatch();
    
            pstmt.setLong(1, 2L);
            pstmt.setString(2, "bar");
            pstmt.addBatch();
    
            int[] ret = pstmt.executeBatch();
    
            assert ret[0] == 1;
            assert ret[1] == 1;
    
            pstmt.close();
            conn.close();
        }
    }
  • 执行SELECT
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.sql.Statement;
    
    public class Main {
    
        private static final String DRIVER_NAME = "com.aliyun.odps.jdbc.OdpsDriver";
    
        public static void main(String[] args) throws SQLException {
            try {
                Class.forName(DRIVER_NAME);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
                System.exit(1);
            }
    
            Connection conn = DriverManager.getConnection(
                "jdbc:odps:<maxcompute endpoint>?project=<maxcompute project>",
                "aliyun accessId", "aliyun accessKey");
            ResultSet rs;
    
            Statement stmt = conn.createStatement();
            String sql = "SELECT * FROM JDBC_TEST";
            stmt.executeQuery(sql);
    
            ResultSet rset = stmt.getResultSet();
            while (rset.next()) {
                System.out.println(String.valueOf(rset.getInt(1)) + "\t" + rset.getString(2));
            }
        }
    }

常见问题

  • 如何查看MaxCompute JDBC driver的日志?
    • MaxCompute JDBC driver的日志默认放在driver Jar包所在的同级目录,文件名为jdbc.log
    • 若代码与driver打包为一个uber Jar,日志将会在uber Jar所在的同级目录。
    MaxCompute JDBC driver的日志记录了对JDBC接口调用的详细信息,包括调用的类名、方法名、行数、参数以及返回值等。通过这些信息,您可以轻松地进行debug。
  • 如何获取MaxCompute Logview URL?

    MaxCompute JDBC driver是基于MaxCompute Java SDK的封装。因此,和MaxCompute客户端,MaxCompute Studio以及Dataworks一样,通过MaxCompute JDBC driver执行SQL时,会生成Logview URL。您可以通过Logview查看任务执行状态,追踪任务进度,获取任务执行结果。Logview URL可以通过properties.log4j进行配置,默认打印到STDERR。

  • 关于连接池与auto-commit

    MaxCompute 本身提供Rest服务,和传统数据库维持长连接的方式十分不同。MaxCompute JDBC driver创建Connection是十分轻量的操作,因此并无必要针对 MaxCompute 的JDBC刻意使用连接池。当然,MaxCompute JDBC也支持使用连接池的场景。

    由于MaxCompute不支持Transaction,每个查询动作都会即时体现到服务端,即默认为auto commit的行为。因此MaxCompute JDBC driver不支持关闭auto-commit模式。