从Java中的Postgres加载非物化数组

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从Java中的Postgres加载非物化数组相关的知识,希望对你有一定的参考价值。

设置:我使用Java连接到Postgres数据库,并尝试加载pg_stats.histogram_bounds统计信息,这是Postgres中的一个数组。我可以用sql.Array将该字段提取为Array histogramBounds = rs.getArray("histogram_bounds");对象。这个对象可以用toString()打印,但我不能访问任何数据(例如调用histogramBounds.getArray()getBaseType等)都生成PSQLException: No results were returned by the query.

documentation,以及各种教程和SE问题表明这应该是有效的。

显然sql.Array对象不直接保存数组数据,而是指向服务器上的数据。从我的实验中我得到了以下MWE,它显示了存储在Postgres中的表中的数组值与以某种方式短暂或非物化的数据值之间的差异(因为我怀疑pg_stats可能是其他内部表的视图)。

MWE:我有两个查询,一个从表中选择数据并且可以工作,另一个直接从查询中选择数据,但不起作用。我在文档中看不出任何原因,他们不应该都工作,我想让第二个版本工作,以获得Java数组[1, 2, 3]。令人困惑的是,toString仍然在第二种情况下正确显示数据,因此必须在某些时候成功加载。

import java.sql.Array;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class PostgresStatsExperiments {
    public static void main(String[] args) throws SQLException {
        Connection conn = DriverManager.getConnection("jdbc:postgresql://localhost/tmp", "admin", "admin");
        Statement stmt = conn.createStatement();

        ResultSet rs = stmt.executeQuery("SELECT name, my_data from array_test;"); // This one works
        //ResultSet rs = stmt.executeQuery("SELECT '{1, 2, 3}' AS my_data;"); // This one does not work.

        while (rs.next()) {
            Array myData = rs.getArray("my_data");
            System.out.println(myData);
            System.out.println(myData.getArray()); // PSQLException here in the second case
        }

        stmt.close();
        rs.close();
        conn.close();
    }
}

array_test看起来像这样:

create table array_test ( name varchar, my_data integer ARRAY[3] );
insert into array_test values ('Alice', '{1, 2, 3}');
insert into array_test values ('Bob', '{4, 5, 6}');

在第二种情况下,我得到以下输出:

{1, 2, 3}
Exception in thread "main" org.postgresql.util.PSQLException: No results were returned by the query.
    at org.postgresql.jdbc2.TypeInfoCache.getPGArrayElement(TypeInfoCache.java:425)
    at org.postgresql.jdbc2.AbstractJdbc2Array.buildArray(AbstractJdbc2Array.java:540)
    at org.postgresql.jdbc2.AbstractJdbc2Array.getArrayImpl(AbstractJdbc2Array.java:171)
    at org.postgresql.jdbc2.AbstractJdbc2Array.getArray(AbstractJdbc2Array.java:128)
    at tmp.PostgresStatsExperiments.main(PostgresStatsExperiments.java:21)

我正在使用Maven的postgresql-9.3-1102-jdbc41.jar,问题也与最新的42.2.4.jre7相同。

答案

在查询SELECT '{1, 2, 3}' AS my_data中,列my_data不是任何数组。它是一个恰好看起来像一个数组的字符串 - 但单引号表示一个text(或varchar)值。

并且因为列的数据类型不是数组,所以调用getArray()会抛出异常。

如果需要数组,则需要将文本值强制转换为数组:

SELECT '{1, 2, 3}'::int[] AS my_data;

或更好:使用显式数组构造函数:

SELECT array[1, 2, 3] AS my_data;

pg_stats.histogram_bounds被定义为anyarray - 它不是一个类型化数组,因为它包含每列的不同数据类型(这意味着在每一行中,因为该表每个表列包含一行)。

显然,JDBC驱动程序无法正常使用anyarray

为了处理Java代码中的值,我认为最简单的方法是将其转换为字符串,然后可以将其转换回文本数组(因为字符串表示是正确的)。

因此,如果您使用以下查询:

select schemaname, tablename, attname, histogram_bounds::text::text[] as histogram_bounds, ..
from pg_stats
where ...

您应该能够检索histogram_bounds列的内容(但是您将所有内容都作为字符串,而不是与列的数据类型匹配的数据类型)。

以上是关于从Java中的Postgres加载非物化数组的主要内容,如果未能解决你的问题,请参考以下文章

在Postgres中的物化视图上创建主键

Postgres中的物化节点之sort节点

是否有一个 postgres 命令来列出/删除所有物化视图?

Postgres:更新与物化视图连接的表?错误:视图无法在物化视图中锁定行

刷新或创建物化视图? Postgres

Postgres 物化视图或 CREATE TABLE AS 如果不增量更新?