更新时间:2025-05-29 GMT+08:00

使用Array类型

以下示例将演示如何使用JDBC驱动的Array类型。

代码运行的前提条件:

  1. 根据实际情况添加gaussdbjdbc.jar包(例如,用户使用IDE执行代码,则需要在本地IDE添加gaussdbjdbc.jar包)。
  2. 连接的数据库兼容模式为A,数据库版本需要大于等于503.0。

示例一,JDBC驱动使用内核table of类型基础示例:

// 认证用的用户名和密码直接写到代码中有很大的安全风险,建议在配置文件或者环境变量中存放(密码应密文存放,使用时解密),确保安全。
// 本示例以用户名和密码保存在环境变量中为例,运行本示例前请先在本地环境中设置环境变量(环境变量名称请根据自身情况进行设置)EXAMPLE_USERNAME_ENV和EXAMPLE_PASSWORD_ENV。
// $ip、$port、database需要用户自行修改。

import com.huawei.gaussdb.jdbc.jdbc.ArrayDescriptor;
import com.huawei.gaussdb.jdbc.jdbc.GaussArray;

import java.sql.*;

public class ArrayTest1 {
    // 以非加密方式创建数据库连接。
    public static Connection getConnection() {
        String username = System.getenv("EXAMPLE_USERNAME_ENV");
        String passwd = System.getenv("EXAMPLE_PASSWORD_ENV");
        String driver = "com.huawei.gaussdb.jdbc.Driver";
        String sourceURL = "jdbc:gaussdb://$ip:$port/database?enableGaussArrayAndStruct=true";
        Connection conn = null;
        try {
            // 加载数据库驱动。
            Class.forName(driver);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }

        try {
            // 创建数据库连接。
            conn = DriverManager.getConnection(sourceURL, username, passwd);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }

        return conn;
    }

    /**
     * 创建前置数据库对象。
     *
     * @param conn conn
     * @throws SQLException An exception occurred while executing the statement
     */
    public static void prepareTestObject(Connection conn) throws SQLException {
        Statement stmt = conn.createStatement();
        stmt.execute("create type test_array is table of int");
        stmt.execute("create or replace function test_func return test_array is\n" +
                "begin\n" +
                "     return test_array(123, 456, 789);\n" +
                "end;");

        stmt.execute("create or replace procedure test_proc(v1 in test_array, v2 out test_array) is\n" +
                "begin\n" +
                "     v2 := v1;\n" +
                "     v2(3) := 456;\n" +
                "end;");
    }

    /**
     * 清理数据库对象。
     *
     * @param conn conn
     * @throws SQLException if an exception occurred while executing the statement
     */
    public static void cleanTestObject(Connection conn) throws SQLException {
        Statement stmt = conn.createStatement();
        stmt.execute("drop procedure test_proc");
        stmt.execute("drop function test_func");
        stmt.execute("drop type test_array");
    }

    /**
     * 使用array对象并的相关接口。
     *
     * @param array instance of Array
     * @throws SQLException if used array failed
     */
    public static void testIntArray(Array array) throws SQLException {
        // 遍历打印元素数组。
        Integer[] elements = (Integer[]) array.getArray();
        for (Integer element : elements) {
            System.out.println(element);
        }

        // 遍历打印部分元素。
        Integer[] someElements = (Integer[]) array.getArray(2, 2);
        for (Integer element : someElements) {
            System.out.println(element);
        }

        // 打印元素类型名。
        System.out.println(array.getBaseTypeName());

        // 打印元素的sqlType是否等于Types.INTEGER。
        System.out.println(array.getBaseType() == Types.INTEGER);
    }

    /**
     * 构造array对象并使用其相关接口。
     *
     * @param conn conn
     * @throws SQLException if create array failed
     */
    public static void testConstructArray(Connection conn) throws SQLException {
        System.out.println("===========  testConstructArray  ===========");
        String typeName = "test_array";
        ArrayDescriptor typeDesc = ArrayDescriptor.getDescriptor(typeName, conn);
        GaussArray array = new GaussArray(typeDesc, new Object[]{1, 2, 3});
        testIntArray(array);
    }

    /**
     * 获取function返回的array对象。
     *
     * @param conn conn
     * @throws SQLException if an exception occurred
     */
    public static void testReturnArrayParam(Connection conn) throws SQLException {
        System.out.println("===========  testReturnArrayParam  ===========");

        // 执行存储过程,获取函数返回的array对象并执行Array相关接口。
        PreparedStatement stmt = conn.prepareStatement("select test_func()");
        ResultSet rs = stmt.executeQuery();
        rs.next();
        Array array = rs.getArray(1);
        testIntArray(array);
    }

    /**
     * 构造array对象并执行入参和出参。
     *
     * @param conn conn
     * @throws SQLException if an exception occurred
     */
    public static void testInputOutputArrayParam(Connection conn) throws SQLException {
        System.out.println("===========  testInputOutputArrayParam  ===========");
        String typeName = "test_array";
        ArrayDescriptor typeDesc = ArrayDescriptor.getDescriptor(typeName, conn);
        GaussArray inArray = new GaussArray(typeDesc, new Object[]{1, 2, 3});

        // 支持出参,需要数据库开启proc_outparam_override参数。
        Statement stmt = conn.createStatement();
        stmt.execute("set behavior_compat_options = 'proc_outparam_override'");

        // 执行存储过程。
        CallableStatement cstmt = conn.prepareCall("{call test_proc(?, ?)}");
        cstmt.setArray(1, inArray);
        cstmt.registerOutParameter(2, Types.ARRAY, typeName);
        cstmt.execute();

        // 获取出参并执行Array相关接口。
        Array outArray = cstmt.getArray(2);
        testIntArray(outArray);
    }

    /**
     * 主程序,逐步调用各静态方法。
     *
     * @param args args
     */
    public static void main(String[] args) throws SQLException {
        // 创建数据库连接。
        Connection conn = getConnection();

        // 创建前置测试对象。
        prepareTestObject(conn);

        // 构造数组对象。
        testConstructArray(conn);

        // 数组返回值。
        testReturnArrayParam(conn);

        // 数组入参和出参。
        testInputOutputArrayParam(conn);

        // 删除测试对象。
        cleanTestObject(conn);
    }
}

上述示例的运行结果为:

===========  testConstructArray  ===========
1
2
3
2
3
int4
true
===========  testReturnArrayParam  ===========
123
456
789
456
789
int4
true
===========  testInputOutputArrayParam  ===========
1
2
456
2
456
int4
true

示例二,Array对象接口使用基础示例:

// 认证用的用户名和密码直接写到代码中有很大的安全风险,建议在配置文件或者环境变量中存放(密码应密文存放,使用时解密),确保安全。
// 本示例以用户名和密码保存在环境变量中为例,运行本示例前请先在本地环境中设置环境变量(环境变量名称请根据自身情况进行设置)EXAMPLE_USERNAME_ENV和EXAMPLE_PASSWORD_ENV。
// $ip、$port、database需要用户自行修改。

import com.huawei.gaussdb.jdbc.jdbc.ArrayDescriptor;
import com.huawei.gaussdb.jdbc.jdbc.GaussArray;

import java.sql.*;

public class ArrayTest2 {
    // 以非加密方式创建数据库连接。
    public static Connection getConnection() {
        String username = System.getenv("EXAMPLE_USERNAME_ENV");
        String passwd = System.getenv("EXAMPLE_PASSWORD_ENV");
        String driver = "com.huawei.gaussdb.jdbc.Driver";
        String sourceURL = "jdbc:gaussdb://$ip:$port/database?enableGaussArrayAndStruct=true";
        Connection conn = null;
        try {
            // 加载数据库驱动。
            Class.forName(driver);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }

        try {
            // 创建数据库连接。
            conn = DriverManager.getConnection(sourceURL, username, passwd);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }

        return conn;
    }

    /**
     * 创建前置数据库对象。
     *
     * @param conn conn
     * @throws SQLException An exception occurred while executing the statement
     */
    public static void prepareTestObject(Connection conn) throws SQLException {
        Statement stmt = conn.createStatement();
        stmt.execute("create type test_array is table of int");
    }

    /**
     * 清理数据库对象。
     *
     * @param conn conn
     * @throws SQLException if an exception occurred while executing the statement
     */
    public static void cleanTestObject(Connection conn) throws SQLException {
        Statement stmt = conn.createStatement();
        stmt.execute("drop type test_array");
    }

    /**
     * 主程序,主流程。
     *
     * @param args args
     */
    public static void main(String[] args) throws SQLException {
        // 创建数据库连接。
        Connection conn = getConnection();

        // 创建前置测试对象。
        prepareTestObject(conn);

        String typeName = conn.getSchema() + ".test_array";
        // 获取test_array类型的类型描述符。
        ArrayDescriptor desc = ArrayDescriptor.getDescriptor(typeName, conn);

        // 根据类型描述符和元素数据创建GaussArray对象。
        GaussArray array = new GaussArray(desc, new Object[]{1, 2, 3});

        // 获取array的类型描述符。
        desc = array.getDescriptor();

        // 类型描述符相关接口使用, 非标准接口。
        // 打印类型名是否等于$currentSchema.test_array
        System.out.println((conn.getSchema() + ".test_array").equals(desc.getSQLTypeName()));
        // 打印类型的sqlType是否等于Types.ARRAY
        System.out.println(desc.getSQLType() == Types.ARRAY);

        // array相关接口使用。
        // 获取并遍历array的元素数组。
        Integer[] elements = (Integer[]) array.getArray();
        for (Integer element : elements) {
            System.out.println(element);
        }

        // 获取并遍历array的部分元素数组。
        elements = (Integer[]) array.getArray(1, 2);
        for (Integer element : elements) {
            System.out.println(element);
        }

        // 打印元素类型名。
        System.out.println(array.getBaseTypeName());

        // 打印元素的sqlType是否等于Types.INTEGER。
        System.out.println(array.getBaseType() == Types.INTEGER);

        // 删除测试对象。
        cleanTestObject(conn);
    }
}

上述示例的运行结果为:

true
true
1
2
3
1
2
int4
true

示例三,元素类型为字符类型时,元素为空串,在传给数据库时会转换为null:

// 认证用的用户名和密码直接写到代码中有很大的安全风险,建议在配置文件或者环境变量中存放(密码应密文存放,使用时解密),确保安全。
// 本示例以用户名和密码保存在环境变量中为例,运行本示例前请先在本地环境中设置环境变量(环境变量名称请根据自身情况进行设置)EXAMPLE_USERNAME_ENV和EXAMPLE_PASSWORD_ENV。
// $ip、$port、database需要用户自行修改。

import com.huawei.gaussdb.jdbc.jdbc.ArrayDescriptor;
import com.huawei.gaussdb.jdbc.jdbc.GaussArray;

import java.sql.*;

public class ArrayTest3 {
    // 以非加密方式创建数据库连接。
    public static Connection getConnection() {
        String username = System.getenv("EXAMPLE_USERNAME_ENV");
        String passwd = System.getenv("EXAMPLE_PASSWORD_ENV");
        String driver = "com.huawei.gaussdb.jdbc.Driver";
        String sourceURL = "jdbc:gaussdb://$ip:$port/database?enableGaussArrayAndStruct=true";
        Connection conn = null;
        try {
            // 加载数据库驱动。
            Class.forName(driver);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }

        try {
            // 创建数据库连接。
            conn = DriverManager.getConnection(sourceURL, username, passwd);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }

        return conn;
    }

    /**
     * 创建前置数据库对象。
     *
     * @param conn conn
     * @throws SQLException An exception occurred while executing the statement
     */
    public static void prepareTestObject(Connection conn) throws SQLException {
        Statement stmt = conn.createStatement();
        stmt.execute("create type test_array is table of varchar(50)");
        stmt.execute("create table test_tab(c1 varchar(50))");
        stmt.execute("create or replace procedure test_proc(v1 in test_array) is\n" +
                "begin\n" +
                "     for i in 1..v1.count loop\n" +
                "         insert into test_tab values(v1(i));\n" +
                "     end loop;\n" +
                "end;");
    }

    /**
     * 清理数据库对象。
     *
     * @param conn conn
     * @throws SQLException if an exception occurred while executing the statement
     */
    public static void cleanTestObject(Connection conn) throws SQLException {
        Statement stmt = conn.createStatement();
        stmt.execute("drop procedure test_proc");
        stmt.execute("drop table test_tab");
        stmt.execute("drop type test_array");
    }

    /**
     * 将带有空元素的Array传给数据库。
     *
     * @param conn conn
     * @throws SQLException if an exception occurred while executing the statement
     */
    public static void inParamWithEmptyElement(Connection conn) throws SQLException {
        String typeName = "test_array";
        // 获取test_array类型的类型描述符。
        ArrayDescriptor desc = ArrayDescriptor.getDescriptor(typeName, conn);
        // 根据类型描述符和元素数据创建GaussArray对象。
        GaussArray array = new GaussArray(desc, new String[]{"", "", null});
        // 遍历并打印元素。
        for (String s : (String[]) array.getArray()) {
            if (s == null) {
                System.out.println("s is null");
            } else {
                if (s.isEmpty()) {
                    System.out.println("s is empty");
                }
            }
        }

        // 传入入参并执行存储过程。
        CallableStatement cstmt = conn.prepareCall("{call test_proc(?)}");
        cstmt.setObject(1, array, Types.ARRAY);
        cstmt.execute();

        // 查询结果。
        PreparedStatement stmt = conn.prepareStatement("select * from test_tab");
        ResultSet rs = stmt.executeQuery();
        while (rs.next()) {
            String s = rs.getString(1);
            if (s == null) {
                System.out.println("s is null");
            } else {
                if (s.isEmpty()) {
                    System.out.println("s is empty");
                }
            }
        }
    }

    /**
     * 主程序,逐步调用各静态方法。
     *
     * @param args args
     */
    public static void main(String[] args) throws SQLException {
        // 创建数据库连接。
        Connection conn = getConnection();

        // 创建前置测试对象。
        prepareTestObject(conn);

        // 将带有空元素的array作为入参传给数据库。
        inParamWithEmptyElement(conn);

        // 删除测试对象。
        cleanTestObject(conn);
    }
}

上述示例的运行结果为:

s is empty
s is empty
s is null
s is null
s is null
s is null

示例四,实际注册的varray类型与存储过程出参的varray类型不一致,实际出参返回的实例类型为存储过程的出参类型:

// 认证用的用户名和密码直接写到代码中有很大的安全风险,建议在配置文件或者环境变量中存放(密码应密文存放,使用时解密),确保安全。
// 本示例以用户名和密码保存在环境变量中为例,运行本示例前请先在本地环境中设置环境变量(环境变量名称请根据自身情况进行设置)EXAMPLE_USERNAME_ENV和EXAMPLE_PASSWORD_ENV。
// $ip、$port、database需要用户自行修改。

import com.huawei.gaussdb.jdbc.jdbc.GaussArray;

import java.sql.*;

public class ArrayTest4 {
    // 以非加密方式创建数据库连接。
    public static Connection getConnection() {
        String username = System.getenv("EXAMPLE_USERNAME_ENV");
        String passwd = System.getenv("EXAMPLE_PASSWORD_ENV");
        String driver = "com.huawei.gaussdb.jdbc.Driver";
        String sourceURL = "jdbc:gaussdb://$ip:$port/database?enableGaussArrayAndStruct=true";
        Connection conn = null;
        try {
            // 加载数据库驱动。
            Class.forName(driver);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }

        try {
            // 创建数据库连接。
            conn = DriverManager.getConnection(sourceURL, username, passwd);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }

        return conn;
    }

    /**
     * 创建前置数据库对象。
     *
     * @param conn conn
     * @throws SQLException An exception occurred while executing the statement
     */
    public static void prepareTestObject(Connection conn) throws SQLException {
        Statement stmt = conn.createStatement();
        stmt.execute("create or replace package test_pkg is\n" +
                "    type t1 is varray(20) of int;\n" +
                "    type t2 is varray(20) of varchar(50);\n" +
                "    procedure proc_out_t1(v out t1);\n" +
                "end test_pkg;");

        stmt.execute("create or replace package body test_pkg is\n" +
                "    procedure proc_out_t1(v out t1) is\n" +
                "    begin\n" +
                "        v := t1(1, 2, 3, 4, 5);\n" +
                "    end;\n" +
                "end test_pkg;");
    }

    /**
     * 清理数据库对象。
     *
     * @param conn conn
     * @throws SQLException if an exception occurred while executing the statement
     */
    public static void cleanTestObject(Connection conn) throws SQLException {
        Statement stmt = conn.createStatement();
        stmt.execute("drop package test_pkg");
    }

    /**
     * 注册重载类型。
     *
     * @param conn conn
     * @throws SQLException if an exception occurred while executing statement
     */
    public static void testRegisterDiffType(Connection conn) throws SQLException {
        // 支持出参,需要数据库开启proc_outparam_override参数。
        Statement stmt = conn.createStatement();
        stmt.execute("set behavior_compat_options = 'proc_outparam_override'");
        CallableStatement cstmt = conn.prepareCall("{call test_pkg.proc_out_t1(?)}");

        // 注册类型为t2。
        cstmt.registerOutParameter(1, Types.ARRAY, "test_pkg.t2");
        cstmt.execute();

        // 实际获取类型为t1 getArray返回类型为Integer[]。
        GaussArray array = (GaussArray)cstmt.getArray(1);
        // 遍历元素。
        for (Integer element : (Integer[]) array.getArray()) {
            System.out.println(element);
        }
    }

    /**
     * 主程序,逐步调用各静态方法。
     *
     * @param args args
     */
    public static void main(String[] args) throws SQLException {
        // 创建数据库连接。
        Connection conn = getConnection();

        // 创建前置测试对象。
        prepareTestObject(conn);

        // 注册不同类型参数。
        testRegisterDiffType(conn);

        // 删除测试对象。
        cleanTestObject(conn);
    }
}

上述示例的运行结果为:

1
2
3
4
5
  • 类型名称大小写敏感,且不支持类型名称有小数点的类型。
  • 类型名称不支持同义词。
  • 支持类型名称格式为schema.package.type、package.type、schema.type、type,若未传入schema名,默认按当前schema处理,若其中的名称包含小数点,需要用双引号包裹。
  • 要想使用内置原生数组类型构造ArrayDescriptor,传入的类型名不能省略schema(当前schema下没有内置原生数组类型)。
  • 元素支持的基础类型为:int2、int4、int8、float4、float8、numeric、bool、bpchar、varchar、nvarchar2、name、text、timestamp、timestamptz、time、timetz、clob、bytea、blob。上述类型可能存在的别名,如:smallint、int、integer、bigint、number、float、boolean、char、varchar2等。
  • 元素支持的自定义类型:数组、集合和record类型。
  • JDBC开启URL参数enableGaussArrayAndStruct后,支持获取集合或数组类型的出参和返回值为Array对象。支持出参还需要数据库额外开启GUC参数behavior_compat_options = 'proc_outparam_override'。
  • 不支持出参或返回值为多维数组;不支持出参或返回值的数组起始下标不为1。
  • JDBC端不支持对类型修饰符进行校验,依赖于数据库校验,例如:
    1. 不支持元素长度校验,如table of varchar(5)类型,构造Array时传入字符串长度大于5,JDBC端不报错。
    2. 不支持元素精度校验,如table of numeric(3)类型,构造Array时传入数值位数大于3,JDBC端不报错。
    3. 不支持元素精度转换,如varray of numeric(3, 1)类型,构造Array时传入数值为2.55,JDBC端不会将其转换为2.6。
    4. 不支持varray长度校验,如varray(2) of int类型,构造Array时传入元素个数大于2,JDBC端不报错。
  • 当传入数据库的array对象包含空元素时,数据库会将这些空元素转换为null。例如:当元素类型为字符类型时,如果元素为空串,传给数据库时会转换为null;当元素类型为CLOB时,如果CLOB元素存储的字符串长度为0,传给数据库时会转换为null;当元素类型为BLOB时,如果BLOB元素存储的二进制数组长度为0,传给数据库时会转换为null;当元素类型为BYTEA时,如果BYTEA元素存储的二进制数组长度为0,传给数据库时会转换为null。

    不推荐在构造的array对象中存储空元素,因为空元素在传给数据库时会转换为null。例如,元素为空串时,会转换为null,具体示例可参考上述示例3。

    当数据库传给客户端的array对象包含空元素时,客户端也会将这些空元素转为null。

  • 不同数组类型之间支持隐式转换,当实际注册的类型与存过出参类型不一致,实际出参返回的实例类型为存过的出参类型,可参考上述示例4。
  • Array接口的相关说明可参考GaussArray对象支持的标准接口下方的说明。
  • ArrayDescriptor为非标准接口,ArrayDescriptor的getSQLType接口固定返回java.sql.Types.ARRAY。

    ArrayDescriptor的getSQLTypeName接口返回的类型名可参考数据类型映射关系

Array标准接口参考:

方法名

返回值类型

throws

支持情况

getBaseTypeName()

String

SQLException

支持

getBaseType()

int

SQLException

支持

getArray()

Object

SQLException

支持

getArray(java.util.Map<String,Class<?>> map)

Object

SQLException

不支持

getArray(long index, int count)

Object

SQLException

支持

getArray(long index, int count, java.util.Map<String,Class<?>> map)

Object

SQLException

不支持

getResultSet()

ResultSet

SQLException

不支持

getResultSet(java.util.Map<String,Class<?>> map)

ResultSet

SQLException

不支持

getResultSet(long index, int count)

ResultSet

SQLException

不支持

getResultSet (long index, int count, java.util.Map<String,Class<?>> map)

ResultSet

SQLException

不支持

free()

void

SQLException

不支持

  • getArray和getArray(long index, int count)接口获取的数组类型可参考数据类型映射关系的JAVA变量类型列。

    例如:元素类型为varchar,getArray接口获取的对象类型为String[]。

    特殊说明:元素类型为int2/smallint,元素存取为Short类型,getArray接口获取的对象类型为Short[]。

  • getBaseType接口获取的sqlType可参考数据类型映射关系的JDBC类型索引列。
  • getBaseTypeName获取的元素类型名可参考数据类型映射关系,其中集合类型、数组类型、record类型的类型名映射规则如下:
    1. 若为package里定义的类型,类型名通常格式为schema.package.type。
      若schema、package、type中有任意名称不符合规则,则类型名格式为"schema"."package"."type":
      • 只能包含字母、数字或下划线。
      • 第一个字符不能是数字。
    2. 若为schema里定义的类型,类型名通常格式为schema.type。
      若schema、type中有任意名称不符合规则,则类型名格式为"schema"."type":
      • 只能包含字母、数字或下划线。
      • 第一个字符不能是数字。
  • 对于getArray(long index, int count)接口:
    1. 起始元素的index为1。
    2. index的取值范围为1-2147483647,index的输入超范围时报错。
    3. count的取值范围为0-2147483647,count的输入超范围时报错。
    4. 若index大于元素个数,则返回空数组(长度为0的数组)。
    5. 若count超出按当前index可获取的最大元素的数量,则返回剩余元素组成的数组。