GrabDuck

5 - ResultSet (набор данных)

:



ResultSet содержит все строки, удовлетворяющие условиям в SQL-выражении и предоставляет доступ к данным в этих строках посредствеом набора get-методов, которые организуют доступ к колонкам текущей строки. Метод ResultSet.next используется для перемещения к следующей строке ResultSet, делая ее текущей.

Набор данных результата (result set) является таблицей с заголовками колонок и соответствующих значений, возвращенных запросом. Например, если мы имеем запрос SELECT a, b, c FROM Table1, то набор результата будет в следующей форме:

        a          b            c
        --------   ---------    -------- 
        12345      Cupertino    CA
        83472      Redmond      WA
        83492      Boston       MA

Следующий фрагмент кода демонстрирует выполнение SQL-запроса, который возвращает коллекцию строк, в которой колонка 1 - это int, колонка 2 - String и колонка 3 - массив байтов:

    
    java.sql.Statement stmt = conn.createStatement();
    ResultSet r = stmt.executeQuery("SELECT a, b, c FROM Table1");
    while (r.next()) 
    {
      // Напечатать значения в текущей строке.
      int i = r.getInt("a");
      String s = r.getString("b");
      float f = r.getFloat("c");
      System.out.println("ROW = " + i + " " + s + " " + f);
    }

5.1.1     Строки и курсоры

ResultSet содержит т.н. курсор, который указывает на текущую строку данных. Каждый раз, когда выполняется метод next, курсор перемещается на одну строку вниз. Изначально курсор спозиционирован перед первой строкой, и первый вызов next премещает его на первую строку (она становится текущей). С каждым успешным вызовом next курсор перемещается вниз на одну строку, начиная с самой верхней в ResultSet.

Курсор сохраняется до тех пор, пока не закроется объект ResultSet или его родительский объект Statement.

В SQL курсор для результирующей таблицы имеет имя. Если БД поддерживает позиционированные обновления или позиционированные удаления, то командам обновления или удаления можно передать в качестве параметра имя курсора. Это имя может быть получено с помощью вызова getCursorName.

Заметим, что не все СУБД поддерживают позиционированные обновления или удаления. Чтобы узнать, поддерживает ли данное соединение эти операции или нет, можно вызвать методы DatabaseMetaData.supportsPositionedDelete и supportsPositionedUpdate.

5.1.2     Колонки

Методы getXXX предоставляют доступ к значениям в колонках в текущей строке. В пределах одной строки значения могут быть считаны в любом порядке, но ради обеспечения большей совместимости рекомендуется считывать их подряд слева направо и делать это только один раз.

Для указания колонки можно использовать либо ее имя, либо ее номер. Например, если вторая колонка объекта ResultSet rs называется "title" и хранит строковое значение, то извлечь его можно одним из двух способов:

    String s = rs.getString("title");
    String s = rs.getString(2);
Имейте ввиду, что колонки нумеруются слева направо, начиная с 1. Имена колонок в вызове методов getXXX нечувствительны к регистру букв.

Вариант с использование имен колонок существует для того, чтобы пользователь задавал методам getXXX те же имена колонок, что он использует в запросе. Если выражение select не указывает имена колонок (например "select * from table1" или в случаях, когда колонка вычисляется) должны использоваться номера колонок. В этих случаях пользователь не может знать наверняка имена колонок.

В нектороых случаях имена двух колонок могут совпадать. Тогда при использовании имен колонок в методах getXXX возвращается значение первой подходящей колонки. Таким образом, чтобы считать значение других колонок с таким же именем, надо использовать индексы колонок. Кроме того, использование индексов немного эффективнее.

Информацию о колонках в ResultSet можно получить с помощтю вызова ResultSet.getMetaData. Возвращаемый объект ResultSetMetaData содержит информацию о количестве, типах и свойствах колонок объекта ResultSet.

Если известно имя колонки, но не ее индекс, то для поиска номера колонки можно использовать метод findColumn.

5.1.3     Типы данных и их преобразование

Методы getXXX пытаются сконвертировать низкоуровневые данные в типы данных языка Java. Например, метод если метод getXXX - это getString и тип данных в БД - VARCHAR, драйвер JDBC сконвертирует VARCHAR в объект String. Возвращаемым из метода getString значением будет Java-объект String.

Следующая таблица показывает, какие типы данных различные методы getXXX могут считывать и какие JDBC-типы (SQL-типы) рекомендуются для этих методов. Знак x означает, что метод getXXX подходит к соответствующему типу данных; знак X означает рекомендуемый метод getXXX для данного типа. Например, значение LONGVARCHAR можно извлечь любым из методов getXXX кроме getBytes и getBinaryStream, но рекомендуется использовать методы getAsciiStream и getUnicodeStream. Метод getObject возвращает любой тип данных как Object и используется в тех случаях, когда соответствующий низкоуровневый тип данных является специфичным для данной СУБД или когда приложению необходимо принять любой тип данных.

Использование методов ResultSet.getXXX при доступе к различным типам данных SQL.
"x" означает, что метод getXXX может быть использован,
"X" означает, что соответствующий метод рекомендуется использовать для этого типа данных.

  T
I
N
Y
I
N
T
S
M
A
L
L
I
N
T
I
N
T
E
G
E
R
B
I
G

N
T

R
E
A
L
F
L
O
A
T
D
O
U
B
L
E
D
E
C
I
M
A
L
N
U
M
E
R
I
C
B
I
T
C
H
A
R
V
A
R
C
H
A
R
L
O
N
G
V
A
R
C
H
A
R
B
I
N
A
R
Y
V
A
R
B
I
N
A
R
Y
L
O
N
G
V
A
R
B
I
N
A
R
Y
D
A
T
E
T
I
M
E
T
I
M
E
S
T
A
M
P
getByte X x x x x x x x x x x x x            
getShort x X x x x x x x x x x x x            
getInt x x X x x x x x x x x x x            
getLong x x x X x x x x x x x x x            
getFloat x x x x X x x x x x x x x            
getDouble x x x x x X X x x x x x x            
getBigDecimal x x x x x x x X X x x x x            
getBoolean x x x x x x x x x X x x x            
getString x x x x x x x x x x X X x x x x x x x
getBytes                           X X x      
getDate                     x x x       X   x
getTime                     x x x         X x
getTimestamp                     x x x       x   X
getAsciiStream                     x x X x x x      
getUnicodeStream                     x x X x x x      
getBinaryStream                           x x X      
getObject x x x x x x x x x x x x x x x x x x x

5.1.4     Использование потоков для очень больших значений

С помощью ResultSet возможно получать очень большие данные типа LONGVARBINARY или LONGVARCHAR. Методы getBytes и getString возвращают эти данные в виде одного большого куска (вплоть до пределов, которые можно узнать с помощью метода Statement.getMaxFieldSize). Тем не менее, может оказаться удобнее считывать очень большие данные небольшими кусками. Это делается с помощью потоков (java.io.InputStream), которые возвращаются некоторыми методами ResultSet. Обратите внимание на то, что к этим потокам надо обращаться сразу, так как они будут закрыты при следующем вызове getXXX объекта ResultSet. (Такое поведение диктуется низкоуровневой реализацией доступа к большим двоичным объектам)

В JDBC API есть три отдельных метода для получения потоков:

  • getBinaryStream возвращает поток байтов "как есть", без какого-либо предварительного преобразования
  • getAsciiStream возвращает поток, состоящий из однобайтовых ASCII-символов.
  • getUnicodeStream возвращает поток двухбайтных символов Unicode.
Эти потоки отличаются от обычных потоков Java, которые возвращают нетипизированные байты, и могут, например, быть ASCII- или Unicode-символами.

Следующий пример демонстрирует использование getAsciiStream:

    java.sql.Statement stmt = con.createStatement();
    ResultSet r = stmt.executeQuery("SELECT x FROM Table2");
    // Теперь считываем колонку 1 результатов кусками по 4 K:
    byte buff = new byte[4096];
    while (r.next()) { 
      Java.io.InputStream fin = r.getAsciiStream(1);
      for (;;) {
        int size = fin.read(buff);
        if (size == -1) { // в конце потока
              break;
        }
        // Отослать заполненный буфер в ASCII-поток:
        output.write(buff, 0, size);
      }
    }

5.1.5     Значения NULL в результатах

Для того, чтобы определить, равно ли значение колонки NULL или нет, надо сначала считать значение колонки, а затем использовать метод ResultSet.wasNull для выяснения этого факта. Значение true означает, что считанное значение равно NULL.

Значение же, возвращаемое в этом случае методом ResultSet.getXXX, равно:

  • null для тех из методов getXXX, которые возвращают объекты (такие методы, как getString, getBigDecimal, getBytes, getDate, getTime, getTimestamp, getAsciiStream, getUnicodeStream, getBinaryStream, getObject).
  • нулевое значение для getByte, getShort, getInt, getLong, getFloat, and getDouble.
  • false в случае getBoolean.

5.1.6     Необязательные или множественные наборы результатов (Result Sets)

Обычно SQL-запросы выполняются либо методом executeQuery (который возвращает единственный ResultSet), либо executeUpdate (который может использоваться для любых запросов на изменение БД и которые возвращают количество измененных строк). Тем не менее, в некоторых случаях приложению заведомо неизвестно, возвратит ли данный запрос результат или нет. Кроме того, некоторые хранимые процедры могут возвратить несколько наборов данных и/или счетчиков обновления.

На этот случай в JDBC есть механизм, когда приложение может обрабатывать произвольную коллекцию наборов результатов или счетчиков обновления. Этот механизм основан на вызове метода execute и последующем вызове трех других методов getResultSet, getUpdateCount и getMoreResults. Эти методы позволяют приложению получать результаты запроса поочереди и для каждого результата определять, является ли он набором данных или счетчиком обновлений.

Нет никакой необходимости закрывать ResultSet; это делается автоматически родительским объектом Statement, когда последний закрывается, выполняется повторно или используется для извлечения следующего результата в последовательности нескольких результатов.


Содержание | Предыдущий | Следующий