高级数据类型
本文你会学到:
- SQL 标准高级数据类型在 JDBC 中的 Java 映射
- ARRAY 数组类型的创建、存储和读取(
createArrayOf / getArray)
- SQLXML 类型的创建和读写操作
- 实际开发中使用高级数据类型的建议
🗺️ 什么场景需要用到高级数据类型?
SQL 标准定义了一系列高级数据类型,JDBC 为这些类型提供了对应的 Java 映射支持。大多数时候 ORM 框架会帮你处理,但当你需要利用数据库原生能力(如 XML 索引查询、数组包含判断)时,了解这些类型就很有价值。主要包括:
| SQL 类型 |
Java 映射 |
用途 |
ARRAY |
java.sql.Array |
存储数组值,如标签列表、ID 集合 |
SQLXML |
java.sql.SQLXML |
存储结构化 XML 数据 |
STRUCT |
java.sql.Struct |
存储自定义结构体 |
DISTINCT |
底层类型的 Java 映射 |
基于已有类型的自定义类型 |
DATALINK |
java.net.URL |
引用外部资源链接 |
实际开发中,这些类型多由 ORM 框架(如 MyBatis、JPA)处理,直接使用 JDBC 操作的场景较少。
📦 ARRAY 数组类型
何时需要用 ARRAY 存储?
SQL ARRAY 类型允许在单个列中存储同构数组值,典型场景包括标签列表、ID 集合、枚举值等。
需要注意的是,并非所有数据库都支持 ARRAY 类型:
- 支持:PostgreSQL、H2、Oracle(VARRAY)
- 不支持:MySQL(可用
JSON 类型替代)
创建并存储 ARRAY
通过 Connection.createArrayOf() 方法可以将 Java 数组包装为 java.sql.Array 对象,再绑定到 PreparedStatement 参数中:
| createArrayOf() 创建数组并存入数据库 |
|---|
| /**
* ARRAY 创建:使用 Connection.createArrayOf() 创建数组并插入数据库
*/
@Test
@DisplayName("ARRAY 创建:通过 createArrayOf 创建数组并插入")
void testArrayCreate() throws SQLException {
// 构造标签数组
String[] tags = {"Java", "JDBC", "Database"};
// 使用 Connection.createArrayOf 创建 java.sql.Array 对象
java.sql.Array sqlArray = conn.createArrayOf("VARCHAR", tags);
// 通过 PreparedStatement.setArray() 插入数组
try (PreparedStatement ps = conn.prepareStatement(
"INSERT INTO products (name, tags) VALUES (?, ?)")) {
ps.setString(1, "JDBC 高级类型教程");
ps.setArray(2, sqlArray);
int affectedRows = ps.executeUpdate();
assertEquals(1, affectedRows, "应成功插入 1 行");
}
// 验证插入结果
try (Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT COUNT(*) FROM products WHERE name = 'JDBC 高级类型教程'")) {
assertTrue(rs.next(), "应有查询结果");
assertEquals(1, rs.getInt(1), "应有 1 条匹配记录");
}
System.out.println("ARRAY 创建测试通过,标签: " + Arrays.toString(tags));
}
|
读取 ARRAY
从 ResultSet 获取 java.sql.Array 对象后,调用 getArray() 转换为 Java 数组:
| getArray() 读取数组并转换为 Java 数组 |
|---|
| /**
* ARRAY 读取:从 ResultSet 获取数组并转换为 Java 数组
*/
@Test
@DisplayName("ARRAY 读取:从 ResultSet 获取数组内容")
void testArrayRead() throws SQLException {
// 先插入测试数据
String[] tags = {"Spring", "Boot", "Security"};
java.sql.Array sqlArray = conn.createArrayOf("VARCHAR", tags);
try (PreparedStatement ps = conn.prepareStatement(
"INSERT INTO products (name, tags) VALUES (?, ?)")) {
ps.setString(1, "Spring Security 实战");
ps.setArray(2, sqlArray);
ps.executeUpdate();
}
// 从 ResultSet 读取数组
try (Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT name, tags FROM products WHERE name = 'Spring Security 实战'")) {
assertTrue(rs.next(), "应有查询结果");
// 使用 getArray() 获取 java.sql.Array 对象
java.sql.Array resultArray = rs.getArray("tags");
assertNotNull(resultArray, "tags 列不应为 null");
// 将 java.sql.Array 转换为 String[](H2 返回 Object[],需手动转换)
Object[] arrayObj = (Object[]) resultArray.getArray();
String[] resultTags = new String[arrayObj.length];
for (int i = 0; i < arrayObj.length; i++) {
resultTags[i] = (String) arrayObj[i];
}
// 打印每个元素
System.out.println("读取到的标签:");
for (String tag : resultTags) {
System.out.println(" - " + tag);
}
// 验证数组内容与插入时一致
assertArrayEquals(tags, resultTags, "读取的数组应与插入时一致");
}
}
|
ARRAY 操作方法速查
| 方法 |
说明 |
Connection.createArrayOf(typeName, elements) |
从 Java 数组创建 java.sql.Array 对象 |
PreparedStatement.setArray(i, array) |
将 Array 绑定到预编译参数 |
ResultSet.getArray(column) |
从结果集获取 java.sql.Array 对象 |
Array.getArray() |
将 ARRAY 转换为 Java Object(通常为 Object[]) |
Array.free() |
释放 ARRAY 资源 |
数据库兼容性
ARRAY 类型的支持因数据库而异。H2 和 PostgreSQL 支持,MySQL 不支持。跨数据库项目需谨慎使用。
📄 SQLXML 类型
什么是 SQLXML
SQLXML 类型用于在数据库中存储 XML 数据,支持 XQuery、XPath 等 XML 操作的数据库(如 Oracle、PostgreSQL)可以充分利用此类型实现高效的 XML 查询和索引。
创建并存储 SQLXML
通过 Connection.createSQLXML() 创建空的 SQLXML 对象,再设置其内容并绑定到 PreparedStatement:
| createSQLXML() 创建并操作 XML 数据 |
|---|
| /**
* SQLXML 演示:创建并操作 SQLXML 对象
* <p>
* 注意:SQLXML 支持因数据库厂商而异。H2 不完全支持 SQLXML,
* 在 MySQL、PostgreSQL、Oracle 等数据库中 SQLXML 通常有更完善的支持。
* 此处使用 CLOB 列存储 XML 文本作为替代方案。
*/
@Test
@DisplayName("SQLXML 演示:创建和读取 XML 数据")
void testSqlXmlWriteRead() throws SQLException {
// 尝试使用 createSQLXML(),H2 可能不支持
String xmlContent = "<root><item>test</item></root>";
try {
// 创建 SQLXML 对象
SQLXML sqlxml = conn.createSQLXML();
sqlxml.setString(xmlContent);
// 插入数据——将 SQLXML 作为字符串写入 CLOB 列
try (PreparedStatement ps = conn.prepareStatement(
"INSERT INTO xml_documents (content) VALUES (?)")) {
ps.setString(1, sqlxml.getString());
int affectedRows = ps.executeUpdate();
assertEquals(1, affectedRows, "应成功插入 1 行");
}
// 释放 SQLXML 资源
sqlxml.free();
// 读取并验证 XML 内容
try (Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT content FROM xml_documents")) {
assertTrue(rs.next(), "应有查询结果");
String resultXml = rs.getString("content");
assertNotNull(resultXml, "XML 内容不应为 null");
assertTrue(resultXml.contains("<item>test</item>"),
"XML 应包含预期的元素内容");
System.out.println("读取到的 XML: " + resultXml);
}
} catch (SQLFeatureNotSupportedException e) {
// H2 不支持 SQLXML 时优雅跳过
assumeTrue(false, "SQLXML not supported: " + e.getMessage());
}
}
|
SQLXML 操作方法速查
| 方法 |
说明 |
Connection.createSQLXML() |
创建空的 SQLXML 对象 |
SQLXML.setString(xml) |
设置 XML 内容(字符串方式) |
SQLXML.setCharacterStream() |
设置 XML 内容(流方式,适合大 XML) |
SQLXML.getString() |
获取 XML 内容为 String |
SQLXML.getCharacterStream() |
获取 XML 内容为 Reader |
PreparedStatement.setSQLXML(i, sqlxml) |
将 SQLXML 绑定到预编译参数 |
SQLXML.free() |
释放 SQLXML 资源 |
数据库兼容性
SQLXML 支持因数据库差异较大。Oracle 和 PostgreSQL 有原生支持,MySQL 需要用 LONGTEXT 存储。使用前务必确认目标数据库的兼容性。
💡 实际开发中的使用建议
- 优先使用 ORM 框架(MyBatis / JPA)处理高级类型映射,避免手写 JDBC 操作
ARRAY 可考虑用**关联表**替代,兼容性更好,且便于建立外键约束
- XML 数据可考虑用 JSON 替代(现代趋势),大多数数据库对 JSON 的支持优于 XML
- 仅在需要利用**数据库原生 XML/数组查询能力**时直接使用 JDBC 高级类型
- 所有大对象(LOB、ARRAY、SQLXML)使用后**务必调用
free() 释放资源**,避免内存泄漏