Verifica di un valore int nullo da un ResultSet Java


277

In Java sto cercando di verificare un valore null, da un ResultSet, in cui la colonna viene eseguita il cast su un 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");
  }
}

Dal frammento di codice sopra, c'è un modo migliore per farlo e suppongo che il secondo test wasNull () sia ridondante?

Educaci e grazie


14
Ho trovato questa domanda perché ho una colonna nullable in un database ed è rappresentata da un numero intero in Java. Si potrebbe pensare che avere una colonna numerica nulla in un database sia abbastanza comune da consentire all'API ResultSet di adattarla un po 'più elegantemente.
spaaarky21

Non sto pubblicando questo come una risposta perché è tangenziale e tutt'altro che universale: la mia solita soluzione a questo è quella di inserire IF(colName = NULL, 0, colName) AS colNamela SELECTdichiarazione (preferibilmente in un proc memorizzato). Filosoficamente, ciò dipende dal fatto che il DB debba essere conforme all'app o viceversa. Poiché SQL gestisce facilmente i NULL e molti utenti SQL non lo fanno (ad esempio java.sql.ResultSet), scelgo di gestirli nel DB quando possibile. (Questo, naturalmente, presuppone che concettualmente NULL e zero siano equivalenti per i tuoi scopi.)
s.co.tt

Risposte:


368

L'impostazione predefinita per ResultSet.getIntquando NULLdeve essere restituito il valore del campo 0, che è anche il valore predefinito per la iValdichiarazione. Nel qual caso il test è completamente ridondante.

Se in realtà vuoi fare qualcosa di diverso se il valore del campo è NULL, ti suggerisco:

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

(Modificato come commenti @martin di seguito; il codice OP così come scritto non verrà compilato perché iValnon inizializzato)


2
Ho appena trovato la stessa dichiarazione nei documenti. Vale la pena un thread separato su SO imho. ( java.sun.com/j2se/1.4.2/docs/api/java/sql/… )
Roman

6
@Roman: vedere javadoc per getInt in ResultSet: "Restituisce: il valore della colonna; se il valore è SQL NULL, il valore restituito è 0"
Cowan,

150
La verità è davvero ridicola. getInt()dovrebbe essere quello getInteger()che restituisce un Integerche è nullse il valore DB è null. Gli sviluppatori hanno davvero rovinato tutto.
ryvantage

7
@sactiw seguendo questa logica, tutto su Java dovrebbe essere stato sviluppato per evitare NPE, che non è il caso. Evitare NPE è responsabilità degli sviluppatori di app, non delle API interne del linguaggio.
Mateus Viccari,

10
OMG Perché, semplicemente, non restituisce NULL 0e NULLsono due cose molto diverse
deFreitas,

84

Un'altra soluzione:

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

27

Penso che sia ridondante. rs.getObject("ID_PARENT")dovrebbe restituire un Integeroggetto oppure null, se il valore della colonna era effettivamente NULL. Quindi dovrebbe anche essere possibile fare qualcosa del tipo:

if (rs.next()) {
  Integer idParent = (Integer) rs.getObject("ID_PARENT");
  if (idParent != null) {
    iVal = idParent; // works for Java 1.5+
  } else {
    // handle this case
  }      
}

5
Hm, almeno nel mio caso, il problema è che la chiamata getObjectnon restituisce necessariamente un numero intero, a causa della natura del tipo di colonna nell'oracolo db che sto usando ("Numero").
Matt Mc

Stesso problema di Matt ... Con MySQL e Types.BIGINT(che dovrebbe essere mappato a Long) il getObject()metodo restituisce 0 invece di null.
Xonya,

2
Potrebbe anche farers.getObject("ID_PARENT", Integer.class)
Arlo l'

26

Controlla se il campo è nullo meno in uso ResultSet#getObject(). Sostituisci -1con qualunque valore di maiuscolo che desideri.

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

Oppure, se puoi garantire di utilizzare il giusto tipo di colonna DB in modo che ResultSet#getObject()restituisca davvero un Integer(e quindi no Long, Shorto Byte), puoi anche semplicemente inserirlo in un Integer.

Integer foo = (Integer) resultSet.getObject("foo");

1
No, ma costruirà un oggetto intero non necessario nel caso non nullo. (E a proposito la maggior parte dei driver JDBC non colpiscono il db durante nessuna chiamata del metodo ResultSet ... in genere non si ottiene il ResultSet fino a quando tutti i dati non sono passati sul filo).
EricS,

Dipende dalle dimensioni del recupero. Nella maggior parte dei driver, l'impostazione predefinita è 10 righe e una volta recuperato il recupero, verranno elaborati, ma il recupero successivo non verrà recuperato fino al termine dell'elaborazione.
Kristo Aun,

Sono sorpreso che la tua risposta non sia la risposta migliore :-) Vorrei votare perché è la risposta corretta che evita il metodo molto pericoloso e pericoloso wasNull (). Per me è un motivo supplementare per smettere di usare Java ;-) e continuare a usare VB.Net dove RecordSet ha risolto questo semplice problema da più di 10 anni!
schlebe,

11

AFAIK puoi semplicemente usare

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

anche se è NULL.


5

Solo un aggiornamento con Java Generics.

È possibile creare un metodo di utilità per recuperare un valore facoltativo di qualsiasi tipo Java da un determinato ResultSet, precedentemente trasmesso.

Sfortunatamente, getObject (columnName, Class) non restituisce null, ma il valore predefinito per un determinato tipo di Java, quindi sono necessarie 2 chiamate

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;
}

In questo esempio, il codice potrebbe essere simile al seguente:

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

Felice di ricevere feedback


2

È possibile chiamare questo metodo utilizzando il resultSet e il nome della colonna con tipo Numero. Restituirà il valore Intero o null. Non verranno restituiti zeri per valore vuoto nel database

private Integer getIntWithNullCheck(ResultSet rset, String columnName) {
    try {
        Integer value = rset.getInt(columnName);
        return rset.wasNull() ? null : value;
    } catch (Exception e) {
        return null;
    }
}

Potete per favore andare più in dettaglio su come questo risolve la domanda?
Sterling Archer,

È possibile chiamare questo metodo utilizzando il resultSet e il nome della colonna con tipo Numero. Restituirà il valore Intero o null. Non verranno restituiti zeri per valore vuoto nel database.
amine kriaa,

Eccellente! Modificalo nella tua risposta (ed elimina il commento dopo la modifica) e avrai una buona risposta :)
Sterling Archer

1

Con java 8 puoi fare questo:

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

In tal caso, si assicura che nVal sarà null (e non zero) se il valore SQL è NULL


2
ma non si applica aresultSet.getInt("col_name")
rapt

1
Con l'origine dati MSSQL, questo non sembra funzionare. Deve avere il controllo aggiuntivo diif (rs.wasNull())
alltej

1

Per comodità, è possibile creare una classe wrapper attorno a ResultSet che restituisce valori null quando ResultSetnormalmente non lo sarebbe.

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;
    }

    // ...

}

-6

Un altro bel modo di controllare, se si ha il controllo di SQL, è quello di aggiungere un valore predefinito nella query stessa per la colonna int. Quindi controlla solo quel valore.

ad es. per un database Oracle, utilizzare NVL

SELECT NVL(ID_PARENT, -999) FROM TABLE_NAME;

quindi controlla

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

Naturalmente anche questo presuppone che esista un valore che normalmente non si trova nella colonna.


12
Ho votato a favore di questa risposta poiché è molto probabile che causi problemi a molte persone. Una colonna int se definita come nullable ha un set di valori costituito da numeri positivi, zero, numeri negativi e NULL. In qualsiasi momento si può semplicemente inserire una riga di dati valida contenente questo numero magico e tutte le cose improvvise andranno male. Fondamentalmente è l'implementazione del modello anti numero magico. Non farlo
Matthias Hryniszak,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.