W3docs

Java JDBC ResultSet

Itere e leia linhas de uma consulta SQL em Java com a interface ResultSet.

Um ResultSet é um cursor sobre as linhas retornadas por uma consulta. Ele não mantém todas as linhas na memória de uma vez; aponta para uma linha por vez e você avança por ele. Importante: um ResultSet recém-criado está posicionado antes da primeira linha — você precisa chamar next() uma vez para chegar à primeira. É por isso que todo laço de leitura usa while (rs.next()).

Você obtém um ResultSet de uma consulta executada por meio de um Statement ou de um PreparedStatement. Esta página explica como percorrer esse cursor e ler colunas com segurança.

O laço de leitura

String sql = "SELECT id, name, score FROM player ORDER BY score DESC";
try (Statement st = conn.createStatement();
     ResultSet rs = st.executeQuery(sql)) {
  while (rs.next()) {                       // advance; false when no more rows
    int id = rs.getInt("id");               // read by column name...
    String name = rs.getString(2);          // ...or by 1-based index
    int score = rs.getInt("score");
    System.out.println(id + " " + name + " " + score);
  }
}

Lendo colunas: por nome ou por índice

Ambas as formas funcionam. Nomes de coluna são mais claros e resistem a uma reordenação do SELECT; índices de coluna (baseados em 1, como tudo no JDBC) são marginalmente mais rápidos. Nomes vencem em manutenibilidade em quase todo código. Se você precisar dos nomes, tipos ou quantidade de colunas em tempo de execução — por exemplo, para renderizar uma tabela genérica — leia-os no ResultSetMetaData obtido com rs.getMetaData().

O problema do NULL e wasNull()

Os getters primitivos não podem retornar null. getInt retorna 0 para um NULL SQL, getDouble retorna 0.0 e assim por diante — indistinguível de um zero real. Quando uma coluna é anulável e a diferença importa, chame rs.wasNull() imediatamente após o getter:

int score = rs.getInt("score");
if (rs.wasNull()) { /* it was SQL NULL, not a zero */ }

(Para tipos de objeto, getObject retorna null diretamente, o que costuma ser mais limpo.)

Tipo de cursor e concorrência

Por padrão, um ResultSet é TYPE_FORWARD_ONLY e CONCUR_READ_ONLY — você avança e apenas lê. Solicite TYPE_SCROLL_INSENSITIVE para chamar previous(), absolute(n) ou first(), ou CONCUR_UPDATABLE para editar linhas no lugar. Esses modos custam mais, portanto solicite-os somente quando necessário.

Feche-o (try-with-resources faz isso)

Um ResultSet mantém um cursor do lado do servidor; deixá-lo aberto desperdiça recursos do banco de dados. Fechar o Statement correspondente o fecha também, e o try-with-resources cuida de ambos. Nunca retorne um ResultSet aberto de um método cuja Connection já foi fechada.

Um exemplo completo: o cursor forward-only e wasNull

Este programa modela o cursor do ResultSet com uma pequena lista em memória — começando antes da primeira linha, avançando com um passo estilo next() — e reproduz o comportamento de getInt retornar 0 para NULL com uma verificação de wasNull, além dos constantes de cursor.

java— editable, runs on the server

O que observar na execução:

  • O cursor começa no índice -1antes da primeira linha — e o passo ++cursor espelha rs.next(): avança primeiro, depois lê. É exatamente por isso que um laço real é while (rs.next()) e nunca lê uma coluna antes do primeiro next().
  • As linhas são lidas uma de cada vez, não carregadas como uma lista. O modelo usa uma lista por simplicidade, mas um ResultSet real transmite linhas do servidor, o que permite lidar com conjuntos de resultados muito maiores que a memória.
  • A pontuação null de Linus foi impressa como 0 — a mesma armadilha que getInt cria. Sem a flag você não poderia distinguir uma pontuação ausente de um zero genuíno.
  • O marcador (wasNull) é o desambiguador. No JDBC real você chama rs.wasNull() logo após o getter, pois ele relata sobre a coluna lida mais recentemente — leia outra coluna antes e a resposta muda.
  • As constantes (TYPE_FORWARD_ONLY, CONCUR_READ_ONLY, FETCH_FORWARD) descrevem o cursor padrão e mais barato: avançar, somente leitura. Você opta por cursores roláveis ou atualizáveis deliberadamente, pois eles pedem mais do servidor.

Prática

Prática
Uma consulta lê uma coluna 'score' anulável com rs.getInt('score') e obtém 0. Como você sabe se o valor era um 0 real ou um NULL SQL?
Uma consulta lê uma coluna 'score' anulável com rs.getInt('score') e obtém 0. Como você sabe se o valor era um 0 real ou um NULL SQL?
Was this page helpful?