Результаты запроса ResultSet

Класс ResultSet представляет результирующий набор данных и обеспечивает приложению построчный доступ к результатам запросов. При обработке запроса ResultSet поддерживает указатель на текущую обрабатываемую строку.

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

Методы ResultSet

Список наиболее часто используемых методов класса ResultSet представлен в таблице :

МетодОписание
public boolean absolute(int row) throws SQLException Метод перемещает курсор на заданное число строк от начала, если число положительно, и от конца - если отрицательно
public void afterLast() throws SQLException Этот метод перемещает курсор в конец результирующего набора за последнюю строку
public void beforeFirst() throws SQLException Этот метод перемещает курсор в начало результирующего набора перед первой строкой
public void deleteRow() throws SQLException Удаляет текущую строку из результирующего набора и базы данных
public ResultSetMetaData getMetaData() throws SQLException Предоставляет объект метаданных для данного ResultSet. Класс ResultSetMetaData содержит информацию о результирующие таблице, такую как количество столбцов, их заголовок и т.д.
public int getRow() throws SQLException Возвращает номер текущей строки
public Statement getStatement() throws SQLException Возвращает экземпляр Statement, который произвел данный результирующий набор
public boolean next() throws SQLException
public boolean previous() throws SQLException
Эти методы позволяют переместиться в результирующем наборе на одну строку вперед или назад. Во вновь созданном результирующем наборе курсор устанавливается перед первой строкой, поэтому первое обращение к методу next() влечет позиционирование на первую строку. Эти методы возвращают true, если остается строка для дальнейшего перемещения. Если строк для обработки больше нет, возвращается false. Если открыт поток InputStream для предыдущей строки, он закрывается. Также очищается цепочка предупреждений SQLWarning
public void close() throws SQLException Осуществляет немедленное закрытие ResultSet вручную. Обычно этого не требуется, так как закрытие Statement, связанного с ResultSet, автоматически закрывает ResultSet. К сожалению, не все разработчики JDBC-драйверов придерживаются этих концепций, например, драйвер Oracle самостоятельно не закрывает ResultSet'ы, так что настоятельно рекомендуется закрывать вручную

Пример использования ResultSet

package mssql;
import java.sql.*;

public class Main
{
    private static Connection con = null;
    private static String username = "name";
    private static String password = "pass";
    private static String URL = "jdbc:jtds:sqlserver://localhost:1433";

    public static void main(String[] args) throws SQLException
    {
        // Загрузка драйвера
        DriverManager.registerDriver(new net.sourceforge.jtds.jdbc.Driver());
        // Подключение к БД
        con = DriverManager.getConnection(URL, username, password);

        if(con != null) 
            System.out.println("Connection Successful !\n");
        else
            System.exit(0);
        // Создание Statement для отправки запроса базе данных
        Statement st = con.createStatement();
		// Результирующий запрос
        ResultSet rs = st.executeQuery("select * from users");
        // Количество колонок в результирующем запросе
        int columns = rs.getMetaData().getColumnCount();
        // Перебор строк с данными
        while(rs.next()){
            for (int i = 1; i <= columns; i++){
                System.out.print(rs.getString(i) + "\t");
            }
            System.out.println();
        }
        System.out.println();
        if(rs != null)
            rs.close();
        if(st != null)
            st.close();
        if(con != null)
            con.close();
    }
}
Результирующий набор данных ResultSet можно не закрывать. Это делается автоматически родительским объектом Statement, когда он закрывается, начинает выполняться повторно или используется для извлечения следующего результата в последовательности нескольких результатов. Но лучше все же закрывать и не надеяться, что это сделает разработчик драйвера JDBC.

Значение NULL в ResultSet

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

В случае, если возвращаемое значение NULL, то методы ResultSet.getXXX, равны:

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

Строки, колонки и курсоры ResultSet

ResultSet содержит так называемый курсор, который позиционируется на текущей строке данных. При вызове метода next, курсор перемещается на следующую строку.

При открытии набора данных ResultSet курсор расположен перед первой строкой, и первый вызов next передвигает его на первую строку.

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

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

Statement stmt = connection.createStatement();
ResultSet rset = stmt.executeQuery ("select * from users");

String cursorName = rs.getCursorName();

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

Методы ResultSet.getXXX предоставляют доступ к значениям в колонках в текущей строке. В пределах одной строки значения могут быть считаны в любом порядке. Для обеспечения бо́льшей совместимости рекомендуется считывать их подряд слева направо и делать это только один раз. Для указания колонки можно использовать либо ее имя, либо ее номер. Например, если вторая колонка объекта ResultSet rs называется "title" и хранит строковое значение, то извлечь его можно одним из двух способов:

String s = rs.getString("title");
String s = rs.getString(2);

При обращении к колонке по номеру следует помнить, что колонки нумеруются слева направо, начиная с 1, а имена колонок в вызове методов getXXX нечувствительны к регистру букв.

Наименования колонок совпадает с соответствующими наименованиями колонок в запросе. Если же в выражении select не указываются имена колонок (например "select * from users"), то необходимо либо использовать номера колонок, либо "подключать" метаданные. Информацию о колонках в ResultSet можно получить с помощью вызова ResultSet.getMetaData. Возвращаемый объект ResultSetMetaData содержит информацию о количестве, типах и свойствах колонок объекта ResultSet.

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

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

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

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

Следующая таблица показывает, какие типы данных различные методы getXXX могут считывать и какие JDBC-типы (SQL-типы) рекомендуются для этих методов.

Условные обозначения :

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

Например, для типа данных LONGVARCHAR значение можно извлечь любым из методов getXXX кроме getBytes и getBinaryStream, но рекомендуется использовать методы getAsciiStream и getUnicodeStream.

Метод getObject возвращает значение как Object и может быть использован в тех случаях, когда соответствующий низкоуровневый тип данных является специфичным для данной СУБД, или когда приложению необходимо принять любой тип данных.

Таблица соответствия методов ResultSet.getXXX при чтении значений различных типам данных SQL.

  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
I
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
getByteX 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

Чтение больших LOB объектов

Для чтения больших объектов LOB (Large Object Bynary) также используется ResultSet. Методы getBytes и getString возвращают эти данные в виде одного большого массива (байт, символов). Можно узнать размер объекта с помощью метода Statement.getMaxFieldSize.

byte[] binaryBuffer = null;
try {
    Statement stmt = connection.createStatement();
    ResultSet rset = stmt.executeQuery(sql);
    rset.next();
    binaryBuffer = rset.getBytes(1);
    rset.close();
    stmt.close();
} catch (SQLException e) {
    System.err.println (e.getMessage());
}

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

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

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

Эти потоки отличаются от обычных потоков Java, которые возвращают нетипизированные байты.

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

String sql = "select book from lib where name = 'Золушка'";
Statement stmt = connection.createStatement();
ResultSet rs = st.executeQuery(sql);
rs.next(); 
Clob clob = rs.getClob(1); 

InputStream is = clob.getAsciiStream(); 

Множественные наборы : getResultSet, getUpdateCount, getMoreResults

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

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

Методы getResultSet, getUpdateCount и getMoreResults позволяют приложению получать результаты запроса по-очереди и для каждого результата определять, является ли он набором данных или счетчиком обновлений.

  Рейтинг@Mail.ru