Статьи

Oracle LONG и LONG RAW вызывают исключение «поток уже закрыт»

Как и многие старые базы данных, Oracle имеет устаревшие типы данных, с которыми довольно сложно работать в повседневной SQL. Обычно вы больше не сталкиваетесь с типами данных LONG и LONG RAW , но когда вы работаете со старой базой данных или представлениями словаря, вам, возможно, придется иметь дело с LONG .

Эти типы данных в значительной степени совпадают с «более новыми» представлениями больших объектов:

  • LONG и CLOB — это несколько одно и то же, за исключением того, что
  • LONG RAW и BLOB — это одно и то же, за исключением того, что

Чтение LONG или LONG RAW из JDBC вызывает исключение «Поток уже закрыт»

Когда у вас есть следующая схема:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
CREATE TABLE t_long_raw_and_blob (
  id        NUMBER(7),
  blob1     BLOB,
  longx     LONG RAW,
  blob2     BLOB,
 
  CONSTRAINT pk_t_long_raw_and_blob PRIMARY KEY (id)
);
 
CREATE TABLE t_long_and_clob (
  id        NUMBER(7),
  clob1     CLOB,
  longx     LONG,
  clob2     CLOB,
 
  CONSTRAINT pk_t_long_and_clob PRIMARY KEY (id)
);

… Вы не можете просто выбрать все столбцы из JDBC (или других API) следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
try (PreparedStatement s = con.prepareStatement(
        "SELECT * FROM t_long_raw_and_blob");
     ResultSet rs = s.executeQuery()) {
 
    while (rs.next()) {
        System.out.println();
        System.out.println("ID    = " + rs.getInt(1));
        System.out.println("BLOB1 = " + rs.getBytes(2));
        System.out.println("LONGX = " + rs.getBytes(3));
        System.out.println("BLOB2 = " + rs.getBytes(4));
    }
}

Если вы делаете выше, вы столкнетесь с чем-то вроде:

1
2
3
4
Caused by: java.sql.SQLException: Stream has already been closed
    at oracle.jdbc.driver.LongRawAccessor.getBytes(LongRawAccessor.java:162)
    at oracle.jdbc.driver.OracleResultSetImpl.getBytes(OracleResultSetImpl.java:708)
    ... 33 more

«Правильным» решением было бы выполнить следующее:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
try (PreparedStatement s = con.prepareStatement(
        "SELECT * FROM t_long_raw_and_blob");
     ResultSet rs = s.executeQuery()) {
 
    while (rs.next()) {
        byte[] longx = rs.getBytes(3);
 
        System.out.println();
        System.out.println("ID    = " + rs.getInt(1));
        System.out.println("BLOB1 = " + rs.getBytes(2));
        System.out.println("LONGX = " + longx);
        System.out.println("BLOB2 = " + rs.getBytes(4));
    }
}

Вкратце: все столбцы LONG или LONG RAW должны быть получены из ResultSet до всех остальных столбцов.

Это противно

Верно! Какой-то недостаток протокола Oracle низкого уровня просочился за пределы JDBC API, что очень прискорбно. Мы не заботимся об этих деталях. Мы должны иметь возможность получать ресурсы в любом порядке.

В jOOQ мы исправили эту проблему # 4820 , поэтому вы можете запустить свое утверждение и упорядочить столбцы в любом порядке:

1
2
3
4
5
6
7
8
9
DSL.using(configuration)
   .select(
       T_LONG_RAR_AND_BLOB.ID,
       T_LONG_RAR_AND_BLOB.BLOB1,
       T_LONG_RAR_AND_BLOB.LONGX,
       T_LONG_RAR_AND_BLOB.BLOB2
   )
   .from(T_LONG_RAR_AND_BLOB)
   .fetch();

jOOQ будет внутренне переупорядочивать столбцы при извлечении их из ResultSet , прозрачно.