Verificando um valor int nulo de um Java ResultSet

277

Em Java, estou tentando testar um valor nulo, de um ResultSet, onde a coluna está sendo convertida em um tipo int primitivo .

int iVal;
ResultSet rs = magicallyAppearingStmt.executeQuery(query);
if (rs.next()) {
  if (rs.getObject("ID_PARENT") != null && !rs.wasNull()) {
    iVal = rs.getInt("ID_PARENT");
  }
}

No fragmento de código acima, existe uma maneira melhor de fazer isso e suponho que o segundo teste wasNull () seja redundante?

Eduque-nos e obrigado

ian_scho
fonte
13
Encontrei essa pergunta porque tenho uma coluna anulável em um banco de dados e é representada por um número inteiro em Java. Você pensaria que ter uma coluna numérica anulável em um banco de dados seria comum o suficiente para que a API ResultSet a acomodasse um pouco mais elegante.
spaaarky21
Não estou postando isso como resposta, porque é tangencial e longe de ser universal: minha solução usual para isso é colocar IF(colName = NULL, 0, colName) AS colNamea SELECTdeclaração (de preferência em um processo armazenado). Filosoficamente, isso se resume a se o DB deve estar em conformidade com o aplicativo ou vice-versa. Como o SQL lida com NULLs facilmente e muitos consumidores de SQL não (ie java.sql.ResultSet), eu opto por lidar com ele no banco de dados sempre que possível. (Isto, naturalmente, assume que NULL conceitualmente e Zero são equivalentes para seus propósitos.)
s.co.tt

Respostas:

368

O padrão para ResultSet.getIntquando o valor do campo é NULLretornar 0, que também é o valor padrão para sua iValdeclaração. Nesse caso, seu teste é completamente redundante.

Se você realmente deseja fazer algo diferente se o valor do campo for NULL, sugiro:

int iVal = 0;
ResultSet rs = magicallyAppearingStmt.executeQuery(query);
if (rs.next()) {
    iVal = rs.getInt("ID_PARENT");
    if (rs.wasNull()) {
        // handle NULL field value
    }
}

(Editado como @martin comenta abaixo; o código OP conforme escrito não seria compilado porque iValnão foi inicializado)

Richard
fonte
2
Acabei de encontrar a mesma declaração nos documentos. Vale a pena uma discussão separada no SO imho. ( java.sun.com/j2se/1.4.2/docs/api/java/sql/… )
Roman
6
@Roman - veja o javadoc para getInt no ResultSet: "Retorna: o valor da coluna; se o valor for SQL NULL, o valor retornado é 0"
Cowan
150
A verdade é, de fato, ridícula. getInt()deve ser o getInteger()que retorna um Integerque é nullse o valor do banco de dados é null. Os desenvolvedores realmente estragaram tudo.
Ryvantage
7
@ sactiw seguindo essa lógica, tudo em java deveria ter sido desenvolvido para evitar o NPE, o que não é o caso. Evitar que o NPE seja de responsabilidade dos desenvolvedores do aplicativo, não das APIs internas do idioma.
Mateus Viccari
10
OMG Por que simplesmente não retorna NULL 0e NULLsão duas grandes coisas diferentes
deFreitas 20/12/16
84

Outra solução:

public class DaoTools {
    static public Integer getInteger(ResultSet rs, String strColName) throws SQLException {
        int nValue = rs.getInt(strColName);
        return rs.wasNull() ? null : nValue;
    }
}
Patrice IMBERT
fonte
27

Eu acho que é redundante. rs.getObject("ID_PARENT")deve retornar um Integerobjeto ou null, se o valor da coluna realmente era NULL. Portanto, deve ser possível fazer algo como:

if (rs.next()) {
  Integer idParent = (Integer) rs.getObject("ID_PARENT");
  if (idParent != null) {
    iVal = idParent; // works for Java 1.5+
  } else {
    // handle this case
  }      
}
Andreas Dolk
fonte
5
Hum, pelo menos no meu caso, o problema com isso é que a chamada getObjectnão retorna necessariamente um número inteiro, devido à natureza do tipo de coluna no oracle db que estou usando ("Number").
Matt Mc
Mesmo problema de Matt ... Com o MySQL e Types.BIGINT(que deve ser mapeado para a Long), o getObject()método retorna 0 em vez de nulo.
xonya
2
Também poderia fazerrs.getObject("ID_PARENT", Integer.class)
Arlo
26

Basta verificar se o campo está nullou não em uso ResultSet#getObject(). Substitua -1pelo valor de nulo desejado.

int foo = resultSet.getObject("foo") != null ? resultSet.getInt("foo") : -1;

Ou, se você pode garantir que você use o tipo de coluna DB direita para que ResultSet#getObject()realmente retorna um Integer(e, portanto, não Long, Shortou Byte), então você também pode simplesmente typecast-lo a um Integer.

Integer foo = (Integer) resultSet.getObject("foo");
BalusC
fonte
1
Não, mas ele construirá um objeto Inteiro desnecessário no caso não nulo. (E a maioria dos drivers JDBC não atinge o banco de dados durante nenhuma chamada do método ResultSet ... geralmente você não obtém o ResultSet de volta até que todos os dados sejam transmitidos).
Erics
Depende do tamanho da busca. Na maioria dos drivers, o padrão é 10 linhas e depois que a busca é recuperada, eles serão processados, mas a próxima busca não será recuperada até que o processamento seja concluído.
precisa
Estou surpreso que sua resposta não seja a melhor :-) Eu voto, porque é a resposta correta que evita o método wasNull () muito inseguro e inseguro. Para mim, é uma razão suplementar parar de usar Java ;-) e continuar usando o VB.Net, onde a RecordSet resolveu esse problema fácil há mais de 10 anos!
schlebe
11

AFAIK você pode simplesmente usar

iVal = rs.getInt("ID_PARENT");
if (rs.wasNull()) {
  // do somthing interesting to handle this situation
}

mesmo que seja NULL.

Peter Tillemans
fonte
5

Apenas uma atualização com o Java Generics.

Você pode criar um método utilitário para recuperar um valor opcional de qualquer tipo Java de um determinado ResultSet, convertido anteriormente.

Infelizmente, getObject (columnName, Class) não retorna nulo, mas o valor padrão para determinado tipo Java, portanto, são necessárias 2 chamadas

public <T> T getOptionalValue(final ResultSet rs, final String columnName, final Class<T> clazz) throws SQLException {
    final T value = rs.getObject(columnName, clazz);
    return rs.wasNull() ? null : value;
}

Neste exemplo, seu código pode ser parecido abaixo:

final Integer columnValue = getOptionalValue(rs, Integer.class);
if (columnValue == null) {
    //null handling
} else {
    //use int value of columnValue with autoboxing
}

É um prazer receber feedback

luchoct
fonte
2

Você pode chamar esse método usando resultSet e o nome da coluna com o tipo Number. Ele retornará o valor Inteiro ou nulo. Não haverá zeros retornados para o valor vazio no banco de dados

private Integer getIntWithNullCheck(ResultSet rset, String columnName) {
    try {
        Integer value = rset.getInt(columnName);
        return rset.wasNull() ? null : value;
    } catch (Exception e) {
        return null;
    }
}
amina kriaa
fonte
Você pode entrar em mais detalhes sobre como isso resolve a questão?
Sterling Archer
Você pode chamar esse método usando resultSet e o nome da coluna com o tipo Number. Ele retornará o valor Inteiro ou nulo. Não haverá zeros retornados para o valor vazio no banco de dados.
kriaa amina
Excelente! Editar que em sua resposta (e apagar o comentário após a edição) e você tem uma resposta boa :)
Sterling Archer
1

Com o java 8, você pode fazer isso:

Long nVal = Optional.ofNullable(resultSet.getBigDecimal("col_name"))
                    .map(BigDecimal::longValue).orElse(null));

Nesse caso, você garante que o nVal será nulo (e não zero) se o valor SQL for NULL

George
fonte
2
mas não se aplica aresultSet.getInt("col_name")
extasiado
1
Com a fonte de dados MSSQL, isso parece não funcionar. Ele precisa ter a verificação adicional deif (rs.wasNull())
alltej
1

Por conveniência, você pode criar uma classe de wrapper em torno de ResultSet que retorne valores nulos quando ResultSetnormalmente não.

public final class ResultSetWrapper {

    private final ResultSet rs;

    public ResultSetWrapper(ResultSet rs) {
        this.rs = rs;
    }

    public ResultSet getResultSet() {
        return rs;
    }

    public Boolean getBoolean(String label) throws SQLException {
        final boolean b = rs.getBoolean(label);
        if (rs.wasNull()) {
            return null;
        }
        return b;
    }

    public Byte getByte(String label) throws SQLException {
        final byte b = rs.getByte(label);
        if (rs.wasNull()) {
            return null;
        }
        return b;
    }

    // ...

}
Jacob Crofts
fonte
-6

Outra boa maneira de verificar, se você controla o SQL, é adicionar um valor padrão na própria consulta para sua coluna int. Em seguida, basta verificar esse valor.

por exemplo, para um banco de dados Oracle, use NVL

SELECT NVL(ID_PARENT, -999) FROM TABLE_NAME;

então verifique

if (rs.getInt('ID_PARENT') != -999)
{
}

Obviamente, isso também pressupõe que exista um valor que normalmente não seria encontrado na coluna.

Chris
fonte
12
Votei negativamente nesta resposta, pois é muito provável que cause problemas a muitas pessoas. Uma coluna int, se definida como anulável, possui um conjunto de valores que consiste em números positivos, zero, números negativos e NULL. A qualquer momento, pode-se simplesmente inserir uma linha de dados válida contendo esse número mágico e, de repente, as coisas ficam ruins. É basicamente a implementação do número mágico anti-padrão. Não faça isso.
Matthias Hryniszak 17/04