使用Struct类型
以下示例将演示如何使用JDBC驱动的Struct类型。
代码运行的前提条件:
- 根据实际情况添加gaussdbjdbc.jar包(例如,用户使用IDE执行代码,则需要在本地IDE添加gaussdbjdbc.jar包)。
- 连接的数据库兼容模式为ORA,数据库版本需要大于等于503.0。
示例一,JDBC驱动使用内核record类型基础示例:
// 认证用的用户名和密码直接写到代码中有很大的安全风险,建议在配置文件或者环境变量中存放(密码应密文存放,使用时解密),确保安全。 // 本示例以用户名和密码保存在环境变量中为例,运行本示例前请先在本地环境中设置环境变量(环境变量名称请根据自身情况进行设置)EXAMPLE_USERNAME_ENV和EXAMPLE_PASSWORD_ENV。 // $ip、$port、database需要用户自行修改。 import com.huawei.gaussdb.jdbc.jdbc.StructDescriptor; import com.huawei.gaussdb.jdbc.jdbc.GaussStruct; import java.sql.*; public class StructTest1 { // 以非加密方式创建数据库连接。 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); System.out.println("Connection succeed!"); } 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(); // 支持出参,需要数据库开启proc_outparam_override参数。 stmt.execute("set behavior_compat_options = 'proc_outparam_override'"); // 创建测试对象。 stmt.execute("create or replace package test_pkg is\n" + " type test_rec is record(col1 int, col2 varchar(50));\n" + " function test_func return test_rec;\n" + " procedure test_proc(v1 in test_rec, v2 out test_rec);\n" + "end test_pkg;"); stmt.execute("create or replace package body test_pkg is\n" + " function test_func return test_rec is\n" + " v test_rec;" + " begin\n" + " v.col1 := 123;\n" + " v.col2 := 'abc';\n" + " return v;\n" + " end;\n" + " procedure test_proc(v1 in test_rec, v2 out test_rec) is\n" + " begin\n" + " v2.col1 := v1.col1 + 1;\n" + " v2.col2 := v1.col2 || 'd';\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"); } /** * 使用struct对象并的相关接口。 * * @param struct instance of Struct * @throws SQLException if used Struct failed */ public static void testStruct(Struct struct) throws SQLException { // 遍历打印元素。 Object[] attributes = struct.getAttributes(); for (Object attribute : attributes) { System.out.println(attribute); } } /** * 构造Struct对象并使用其相关接口, 非标准构造方法。 * * @param conn conn * @throws SQLException if create Struct failed */ public static void testConstructStruct1(Connection conn) throws SQLException { System.out.println("=========== testConstructStruct1 ==========="); String typeName = "test_pkg.test_rec"; StructDescriptor typeDesc = StructDescriptor.getDescriptor(typeName, conn); GaussStruct struct = new GaussStruct(typeDesc, new Object[]{666, "aaabbbccc"}); testStruct(struct); } /** * 构造Struct对象并使用其相关接口, 标准接口构造。 * * @param conn conn * @throws SQLException if create Struct failed */ public static void testConstructStruct2(Connection conn) throws SQLException { System.out.println("=========== testConstructStruct2 ==========="); String typeName = "test_pkg.test_rec"; Struct struct = conn.createStruct(typeName, new Object[]{666, "aaabbbccc"}); testStruct(struct); } /** * 获取function返回的Struct对象。 * * @param conn conn * @throws SQLException if an exception occurred */ public static void testReturnStructParam(Connection conn) throws SQLException { System.out.println("=========== testReturnStructParam ==========="); // 执行存储过程,获取函数返回的Struct对象并执行Struct相关接口。 PreparedStatement stmt = conn.prepareStatement("select test_pkg.test_func()"); ResultSet rs = stmt.executeQuery(); rs.next(); Struct Struct = (Struct) rs.getObject(1); testStruct(Struct); } /** * 构造Struct对象并执行入参和出参。 * * @param conn conn * @throws SQLException if an exception occurred */ public static void testInputOutputStructParam(Connection conn) throws SQLException { System.out.println("=========== testInputOutputStructParam ==========="); String typeName = "test_pkg.test_rec"; StructDescriptor typeDesc = StructDescriptor.getDescriptor(typeName, conn); GaussStruct inStruct = new GaussStruct(typeDesc, new Object[]{123, "abc"}); // 执行存储过程。 CallableStatement cstmt = conn.prepareCall("{call test_pkg.test_proc(?, ?)}"); cstmt.setObject(1, inStruct); cstmt.registerOutParameter(2, Types.STRUCT, typeName); cstmt.execute(); // 获取出参并执行Struct相关接口。 Struct outStruct = (Struct) cstmt.getObject(2); testStruct(outStruct); } /** * 主程序,逐步调用各静态方法。 * * @param args args */ public static void main(String[] args) throws SQLException { // 创建数据库连接。 Connection conn = getConnection(); // 创建前置测试对象。 prepareTestObject(conn); // 构造Struct对象方式1。 testConstructStruct1(conn); // 构造Struct对象方式2。 testConstructStruct2(conn); // Struct返回值。 testReturnStructParam(conn); // Struct入参和出参。 testInputOutputStructParam(conn); // 删除测试对象。 cleanTestObject(conn); } }
上述示例的运行结果为:
=========== testConstructStruct1 =========== 666 aaabbbccc =========== testConstructStruct2 =========== 666 aaabbbccc =========== testReturnStructParam =========== 123 abc =========== testInputOutputStructParam =========== 124 abcd
示例二,Struct对象接口使用基础示例:
// 认证用的用户名和密码直接写到代码中有很大的安全风险,建议在配置文件或者环境变量中存放(密码应密文存放,使用时解密),确保安全。 // 本示例以用户名和密码保存在环境变量中为例,运行本示例前请先在本地环境中设置环境变量(环境变量名称请根据自身情况进行设置)EXAMPLE_USERNAME_ENV和EXAMPLE_PASSWORD_ENV。 // $ip、$port、database需要用户自行修改。 import com.huawei.gaussdb.jdbc.jdbc.GaussStruct; import com.huawei.gaussdb.jdbc.jdbc.StructDescriptor; import java.sql.*; public class StructTest2 { // 以非加密方式创建数据库连接。 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 test_rec is record(col1 int, col2 varchar(50));\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 args args */ public static void main(String[] args) throws SQLException { // 创建数据库连接。 Connection conn = getConnection(); // 创建前置测试对象。 prepareTestObject(conn); String typeName = conn.getSchema() + ".test_pkg.test_rec"; // 获取test_array类型的类型描述符。 StructDescriptor desc = StructDescriptor.getDescriptor(typeName, conn); // 根据类型描述符和元素数据创建GaussStruct对象。 GaussStruct struct = new GaussStruct(desc, new Object[]{123, "abc"}); // 获取record的类型描述符。 desc = struct.getDescriptor(); // 类型描述符相关接口使用, 非标准接口。 // 打印类型名是否等于$currentSchema.test_pkg.test_rec System.out.println(typeName.equals(desc.getSQLTypeName())); // 打印类型的sqlType是否等于Types.STRUCT。 System.out.println(desc.getSQLType() == Types.STRUCT); // Struct相关接口使用。 // 获取并遍历struct的元素。 Object[] attributes = struct.getAttributes(); for (Object attribute : attributes) { System.out.println(attribute); } // 删除测试对象。 cleanTestObject(conn); } }
上述示例的运行结果为:
true true 123 abc
示例三,元素类型为字符类型时,元素为空串,在传给数据库时会转换为null:
// 认证用的用户名和密码直接写到代码中有很大的安全风险,建议在配置文件或者环境变量中存放(密码应密文存放,使用时解密),确保安全。 // 本示例以用户名和密码保存在环境变量中为例,运行本示例前请先在本地环境中设置环境变量(环境变量名称请根据自身情况进行设置)EXAMPLE_USERNAME_ENV和EXAMPLE_PASSWORD_ENV。 // $ip、$port、database需要用户自行修改。 import java.sql.*; public class StructTest3 { // 以非加密方式创建数据库连接。 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 table test_tab(c1 varchar(50))"); stmt.execute("create or replace package test_pkg is\n" + " type test_rec is record(col1 varchar(50), col2 varchar(50), col3 varchar(50));\n" + " procedure test_proc(v1 in test_rec);\n" + "end test_pkg;"); stmt.execute("create or replace package body test_pkg is\n" + " procedure test_proc(v1 in test_rec) is\n" + " begin\n" + " insert into test_tab values(v1.col1);\n" + " insert into test_tab values(v1.col2);\n" + " insert into test_tab values(v1.col3);\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"); stmt.execute("drop table test_tab"); } /** * 将带有空元素的Struct传给数据库。 * * @param conn conn * @throws SQLException if an exception occurred while executing the statement */ public static void inParamWithEmptyElement(Connection conn) throws SQLException { String typeName = "test_pkg.test_rec"; // 创建Struct对象。 Struct struct = conn.createStruct(typeName, new Object[]{"", "", null}); // 遍历并打印元素。 for (Object attribute : struct.getAttributes()) { if (attribute == null) { System.out.println("attribute is null"); } else { if (((String) attribute).isEmpty()) { System.out.println("attribute is empty"); } } } // 传入入参并执行存储过程。 CallableStatement cstmt = conn.prepareCall("{call test_pkg.test_proc(?)}"); cstmt.setObject(1, struct, Types.STRUCT); 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); // 将带有空元素的struct作为入参传给数据库。 inParamWithEmptyElement(conn); // 删除测试对象。 cleanTestObject(conn); } }
上述示例的运行结果为:
attribute is empty attribute is empty attribute is null s is null s is null s is null

- 类型名称大小写敏感,且不支持类型名称有小数点的类型。
- 类型名称不支持同义词。
- 支持类型名称格式为schema.package.type、package.type、schema.type、type,若未传入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后,支持获取集合或数组类型的出参和返回值为Struct对象。支持出参还需要数据库额外开启GUC参数behavior_compat_options = 'proc_outparam_override'。
- JDBC端不支持对类型修饰符进行校验,依赖于数据库校验,例如:
- 不支持元素精度转换,如record(c1 numeric(3, 1), c2 numeric(3, 1))类型,构造Struct时传入的列元素数值为2.55,JDBC端不会将其转换为2.6。
- 不支持元素长度校验,如record(c1 varchar(5), c2 varchar(5))类型,构造Struct时传入字符串长度大于5,JDBC端不报错。
- 不支持not null校验,如record(c1 int not null, c2 int)类型,构造Struct时第一列元素为null,JDBC端不报错。
-
当传入数据库的struct对象包含空元素时,数据库会将这些空元素转换为NULL。例如:当元素类型为字符类型时,如果元素为空串,传给数据库时会转换为null;当元素类型为CLOB时,如果CLOB元素存储的字符串长度为0,传给数据库时会转换为null;当元素类型为BLOB时,如果BLOB元素存储的二进制数组长度为0,传给数据库时会转换为null;当元素类型为BYTEA时,如果BYTEA元素存储的二进制数组长度为0,传给数据库时会转换为null。
不推荐在构造的struct对象中存储空元素,因为空元素在传给数据库时会转换为null。例如,元素为空串时,会转换为null,具体示例可参考上述示例3。
当数据库传给客户端的struct对象包含空元素时,客户端也会将这些空元素转为null。
- Struct接口的相关说明可参考GaussStruct对象支持的标准接口下方的说明。
- StructDescriptor为非标准接口,StructDescriptor的getSQLType接口固定返回java.sql.Types.STRUCT。
StructDescriptor的getSQLTypeName接口返回的类型名可参考数据类型映射关系。
Struct标准接口参考:
方法名 |
返回值类型 |
throws |
支持情况 |
---|---|---|---|
getSQLTypeName() |
String |
SQLException |
支持 |
getAttributes() |
Object[] |
SQLException |
支持 |
getAttributes(java.util.Map<String,Class<?>> map) |
Object[] |
SQLException |
不支持 |

- getAttribute接口获取的对象类型为Object[],返回的数组中每个元素的类型可参考数据类型映射关系的JAVA变量类型列。
例如:对于record(c1 int, c2 varchar(5))类型,getAttribute返回Object[]中第一个元素为null或者Integer类型数据,第二个元素为null或者String类型数据。
特殊说明:元素类型为int2/smallint,元素存取为Short类型。
- getSQLTypeName接口返回的类型名可参考数据类型映射关系,其中集合类型、数组类型、record类型的类型名映射规则如下:
- 若为package里定义的类型,类型名通常格式为schema.package.type。
- 若为schema里定义的类型,类型名通常格式为schema.type。