Java 程序在检索包含CLOB的100行后挂起

Java 程序在检索包含CLOB的100行后挂起,java,h2,Java,H2,我正在从“远程”H2数据库(实际上在本地驱动器上,但使用tcp访问)的表中检索一个文本列(CLOB),检索前100行后,程序将挂起检索结果集的下一行。另一方面,如果我访问与嵌入式数据库相同的数据库,则没有问题。如果我尝试使用H2的控制台应用程序使用服务器(即tcp)方法访问数据库来显示表的行,则会收到以下错误消息: IO Exception: "java.io.IOException: org.h2.message.DbException: The object is already close

我正在从“远程”H2数据库(实际上在本地驱动器上,但使用tcp访问)的表中检索一个文本列(CLOB),检索前100行后,程序将挂起检索结果集的下一行。另一方面,如果我访问与嵌入式数据库相同的数据库,则没有问题。如果我尝试使用H2的控制台应用程序使用服务器(即tcp)方法访问数据库来显示表的行,则会收到以下错误消息:

IO Exception: "java.io.IOException: org.h2.message.DbException: The object is already closed [90007-164]"; 
"lob: null table: 14 id: 1" [90031-164] 90031/90031
这是节目单。如果我取消对设置系统属性的调用的注释,程序就会工作。我还尝试使用字符流或简单地调用getString来检索列,由常量USE\u stream控制。结果没有差异:

import java.sql.*;
import java.util.*;
import java.io.*;

public class Jdbc4
{
    private static final boolean USE_STREAM = false;

    public static void main(String[] args) throws Exception
    {
        //System.setProperty("h2.serverResultSetFetchSize", "50");
        Connection conn = null;
        try {
            Class.forName("org.h2.Driver").newInstance();
            conn = DriverManager.getConnection("jdbc:h2:tcp://localhost/file:C:/h2/db/test/test;IFEXISTS=TRUE", "sa", "");
            Statement stmt = conn.createStatement();
            String sql = "select select_variables from ipm_queues";
            ResultSet rs = stmt.executeQuery(sql);
            int count = 0;
            while (rs.next()) {
                ++count;
                String s;
                if (USE_STREAM) {
                    Clob clob = rs.getClob(1);
                    Reader rdr = clob.getCharacterStream();
                    char[] cbuf = new char[1024];
                    StringBuffer sb = new StringBuffer();
                    int len;
                    while ((len = rdr.read(cbuf, 0, cbuf.length)) != -1)
                        sb.append(cbuf, 0, len);
                    rdr.close();
                    s = sb.toString();
                    clob.free();
                }
                else
                    s = rs.getString(1);
                System.out.println(count + ": " + s);
            }
        }
        finally {
            if (conn != null)
                conn.close();
        }
    }
}
下面是用于创建表的DDL(您可以看到它最初是一个MySql表):


您可能应该提供更多详细信息,但您是否检查了网络连接?当连接(或网络连接)试图获取太多数据时,您的数据库服务器可能正在阻止连接。这可能是一种“某种”保护。

我相信我理解挂起的原因。我研究了使用h2.serverResultSetFetchSize值600的最简单情况,该值大于已知的523行。正如我提到的,我可以检索前3行(单个CLOB列),然后我要么挂起第4行的检索,要么得到一个“对象已关闭”异常

事实证明,包含前三列的实际字符串的长度似乎很短,类org.h2.value.ValueLobDb中的方法getInputStream已经有了数据,只返回基于该数据构造的ByteArrayInputStream。第4行的数据仍然在服务器端,因此必须构建一个实际的RemoteInputStream来处理从服务器端LOB获取的数据

问题似乎出在这里:类org.h2.server.TcpServerThread正在SmallLRUCache实例中缓存这些LOB。此缓存似乎设计为仅维护最近引用最少的LOB!!!此缓存的默认大小由系统属性h2.serverCachedObjects提供,默认为64,而默认的获取大小为100。因此,即使我没有重写默认的h2.serverResultSetFetchSize属性,如果我的所有行都有足够大的列需要缓存的LOB,任何fetch size>64都会导致表示第一行的LOB从缓存中清除,我甚至无法检索第一行


LRU缓存似乎是用于保存活动结果集中的LOB的错误结构。当然,默认缓存大小小于默认获取大小似乎不太理想。

该表有523行。当我启动应用程序并指定系统属性h2.serverResultSetFetchSize的值为600时,它仅检索了3行就挂起了!当我用值1重新运行指定此属性时,它不再挂起!高达87的值似乎可以正常工作。返回88行后,值88挂起。Yohann,我不确定“检查我的网络连接”是什么意思。我正在连接到“localhost”,而我的“数据库服务器”只是一个普通的H2安装,对此我几乎没有经验。我应该能够检索这个表的所有523行,并且我可以通过更改我不完全理解的系统参数来检索。我不是说存在网络问题。我的意思是,当服务器认为必须发送太多数据时,它可能会挂起jdbc连接。您是否也可以发布用于读取数据的代码?错误消息“对象已关闭”听起来像是一个意外的JDBC调用序列(我不是说不正确,只是意外)。如果看不到应用程序代码,很难说问题出在哪里。“对象已读取关闭”是H2控制台应用程序在输入查询时生成的错误消息:从ipm_队列中选择*;(错误消息不是来自我的任何代码)源代码和DDL证明有用吗?还有人在调查这件事吗?提前感谢。是的,这是一个bug,应该在下一版本的H2中修复。我刚刚使用H2控制台重新测试了1.3.166,以从ipm_队列检索行,但仍然失败:从ipm_队列中选择*;IO异常:“java.IO.IOException:org.h2.message.DbException:对象已关闭[90007-166]”;“lob:null表:465 id:55034”[90031-166]90031/90031(帮助)。在这种情况下,每行都有多个CLOB列,因此简单地增加缓存的大小是行不通的。我认为您必须认识到,LRU缓存不可能是此功能的正确数据结构。您是对的,它不会在所有情况下都工作。更好的解决方案会更好。欢迎使用补丁:-)我想在离线时进一步讨论这个问题。如果你有兴趣,你可以在ronaa@optonline.net.@托马斯·穆勒:我想在线下进一步讨论这个问题。如果你有兴趣,你可以在ronaa@optonline.net.
CREATE TABLE `ipm_queues` (
    `oid` bigint NOT NULL,
    `queue_id` varchar(256) NOT NULL,
    `store_id` bigint NOT NULL,
    `creation_time` datetime NOT NULL,
    `status` bigint NOT NULL,
    `deleted` bigint NOT NULL,
    `last_mod_time` datetime NOT NULL,
    `queue_name` varchar(128),
    `select_variables` text,
    `where_clause` text,
    `from_table` varchar(128),
    `order_by` varchar(256),
    `from_associate_table` varchar(256),
    `from_view` varchar(128)
);

ALTER TABLE ipm_queues
    ADD CONSTRAINT ipm_queues_pkey PRIMARY KEY (oid);

CREATE UNIQUE INDEX ipm_queues_key_idx ON ipm_queues(queue_id, store_id);

CREATE INDEX ipm_queues_str_idx ON ipm_queues(store_id);