Überprüfen eines Java ResultSet auf einen Null-Int-Wert

277

In Java versuche ich, aus einem ResultSet, in dem die Spalte in einen primitiven int- Typ umgewandelt wird, auf einen Nullwert zu testen .

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

Gibt es aus dem obigen Codefragment einen besseren Weg, dies zu tun, und ich gehe davon aus, dass der zweite wasNull () -Test redundant ist?

Erziehe uns und danke

ian_scho
quelle
13
Ich habe diese Frage gefunden, weil ich eine nullfähige Spalte in einer Datenbank habe und diese in Java durch eine Ganzzahl dargestellt wird. Sie würden denken, dass eine nullfähige numerische Spalte in einer Datenbank häufig genug ist, um von der ResultSet-API etwas eleganter aufgenommen zu werden.
spaaarky21
Ich poste dies nicht als Antwort, weil es tangential und alles andere als universell ist: Meine übliche Lösung besteht darin, IF(colName = NULL, 0, colName) AS colNamedie SELECTAnweisung einzugeben (vorzugsweise in einem gespeicherten Prozess). Philosophisch kommt es darauf an, ob die DB der App entsprechen soll oder umgekehrt. Da SQL NULL-Werte problemlos handhabt und viele SQL-Konsumenten dies nicht tun (dh java.sql.ResultSet), entscheide ich mich, sie nach Möglichkeit in der Datenbank zu behandeln. (Dies setzt natürlich voraus, dass konzeptionell NULL und Null für Ihre Zwecke gleichwertig sind.)
s.co.tt

Antworten:

368

Der Standardwert für ResultSet.getIntden Feldwert ist NULLdie Rückgabe 0. Dies ist auch der Standardwert für Ihre iValDeklaration. In diesem Fall ist Ihr Test vollständig redundant.

Wenn Sie tatsächlich etwas anderes tun möchten, wenn der Feldwert NULL ist, schlage ich vor:

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

(Bearbeitet als @martin-Kommentare unten; der geschriebene OP-Code würde nicht kompiliert, da er iValnicht initialisiert ist.)

Richard
quelle
2
Ich habe gerade die gleiche Aussage in Dokumenten gefunden. Es lohnt sich ein separater Thread auf SO imho. ( java.sun.com/j2se/1.4.2/docs/api/java/sql/… )
Roman
6
@ Roman - siehe javadoc für getInt in ResultSet: "Rückgabe: der Spaltenwert; wenn der Wert SQL NULL ist, ist der zurückgegebene Wert 0"
Cowan
150
Die Wahrheit ist in der Tat lächerlich. getInt()sollte sein, getInteger()was ein zurückgibt Integer, nullwenn der DB-Wert ist null. Die Entwickler haben das wirklich durcheinander gebracht.
Ryvantage
7
@sactiw Nach dieser Logik sollte alles auf Java entwickelt worden sein, um NPE zu vermeiden, was nicht der Fall ist. Vermeiden Sie, dass NPE in der Verantwortung der App-Entwickler liegt, nicht der sprachinternen APIs.
Mateus Viccari
10
OMG Warum hat, einfach nicht NULL zurückgegeben, 0und NULLsind zwei große verschiedene Dinge
deFreitas
84

Eine andere Lösung:

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
quelle
27

Ich denke, es ist überflüssig. rs.getObject("ID_PARENT")sollte ein IntegerObjekt zurückgeben oder null, wenn der Spaltenwert tatsächlich war NULL. Es sollte also sogar möglich sein, etwas zu tun wie:

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
quelle
5
Hm, zumindest in meinem Fall besteht das Problem darin, dass der Aufruf getObjectaufgrund der Art des Spaltentyps in der von mir verwendeten Oracle-Datenbank ("Nummer") nicht unbedingt eine Ganzzahl zurückgibt.
Matt Mc
Gleiches Problem von Matt ... Mit MySQL und Types.BIGINT(das sollte a zugeordnet werden Long) gibt die getObject()Methode 0 anstelle von null zurück.
Xonya
2
Könnte auch tunrs.getObject("ID_PARENT", Integer.class)
Arlo
26

Überprüfen Sie einfach, ob das Feld verwendet wird nulloder nicht ResultSet#getObject(). Ersetzen Sie -1durch einen beliebigen Null-Fall-Wert.

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

Oder, wenn Sie können garantieren , dass Sie den richtigen DB Spaltentyp verwenden , so dass ResultSet#getObject()wirklich eine gibt Integer(und somit nicht Long, Shortoder Byte), dann können Sie auch Typumwandlung es nur einen Integer.

Integer foo = (Integer) resultSet.getObject("foo");
BalusC
quelle
1
Nein, aber es wird ein unnötiges Integer-Objekt im Nicht-Null-Fall erstellt. (Übrigens, die meisten JDBC-Treiber treffen die Datenbank bei keinem ResultSet-Methodenaufruf überhaupt ... Im Allgemeinen erhalten Sie das ResultSet erst zurück, wenn alle Daten über das Kabel eingegangen sind.)
EricS
Dies hängt von der Abrufgröße ab. In den meisten Treibern beträgt der Standardwert 10 Zeilen. Sobald der Abruf abgerufen wurde, werden sie verarbeitet. Der nächste Abruf wird jedoch erst nach Abschluss der Verarbeitung abgerufen.
Kristo Aun
Ich bin überrascht, dass Ihre Antwort nicht die bessere Antwort ist :-) Ich stimme ab, weil es die richtige Antwort ist, die die sehr knifflige unsichere wasNull () -Methode vermeidet. Für mich ist es ein zusätzlicher Grund, Java nicht mehr zu verwenden ;-) und weiterhin VB.Net zu verwenden, wo RecordSet dieses einfache Problem seit mehr als 10 Jahren gelöst hat!
Schlebe
11

AFAIK können Sie einfach verwenden

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

selbst wenn es NULL ist.

Peter Tillemans
quelle
5

Nur ein Update mit Java Generics.

Sie können eine Dienstprogrammmethode erstellen, um einen optionalen Wert eines beliebigen Java-Typs aus einem zuvor erstellten ResultSet abzurufen.

Leider gibt getObject (columnName, Class) nicht null zurück, sondern den Standardwert für den angegebenen Java-Typ, sodass 2 Aufrufe erforderlich sind

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 diesem Beispiel könnte Ihr Code wie folgt aussehen:

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

Freut mich über Feedback

Luchoct
quelle
2

Sie können diese Methode mit dem resultSet und dem Spaltennamen vom Typ Number aufrufen. Es wird entweder der Integer-Wert oder null zurückgegeben. Für den leeren Wert in der Datenbank werden keine Nullen zurückgegeben

private Integer getIntWithNullCheck(ResultSet rset, String columnName) {
    try {
        Integer value = rset.getInt(columnName);
        return rset.wasNull() ? null : value;
    } catch (Exception e) {
        return null;
    }
}
Amin kriaa
quelle
Können Sie bitte näher darauf eingehen, wie dies die Frage löst?
Sterling Archer
Sie können diese Methode mit dem resultSet und dem Spaltennamen vom Typ Number aufrufen. Es wird entweder der Integer-Wert oder null zurückgegeben. Für den leeren Wert in der Datenbank werden keine Nullen zurückgegeben.
Amin Kriaa
Ausgezeichnet! Bearbeiten Sie das in Ihrer Antwort (und löschen Sie den Kommentar nach der Bearbeitung) und Sie haben selbst eine gute Antwort :)
Sterling Archer
1

Mit Java 8 können Sie dies tun:

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

In diesem Fall stellen Sie sicher, dass nVal null (und nicht null) ist, wenn der SQL-Wert NULL ist

George
quelle
2
gilt aber nicht fürresultSet.getInt("col_name")
Rapt
1
Mit der MSSQL-Datenquelle scheint dies nicht zu funktionieren. Es muss die zusätzliche Überprüfung vonif (rs.wasNull())
alltej
1

Der Einfachheit halber können Sie eine Wrapper-Klasse um ResultSet erstellen, die Nullwerte zurückgibt, wenn dies ResultSetnormalerweise nicht der Fall ist .

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
quelle
-6

Eine andere gute Möglichkeit zu überprüfen, ob Sie die SQL-Kontrolle haben, besteht darin, der Abfrage selbst einen Standardwert für Ihre int-Spalte hinzuzufügen. Dann überprüfen Sie einfach diesen Wert.

Verwenden Sie beispielsweise für eine Oracle-Datenbank NVL

SELECT NVL(ID_PARENT, -999) FROM TABLE_NAME;

dann überprüfe

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

Dies setzt natürlich auch voraus, dass es einen Wert gibt, der normalerweise nicht in der Spalte gefunden wird.

Chris
quelle
12
Ich habe diese Antwort abgelehnt, da sie sehr wahrscheinlich vielen Menschen Probleme bereiten wird. Eine int-Spalte hat, wenn sie als nullable definiert ist, eine Reihe von Werten, die aus positiven Zahlen, null, negativen Zahlen und NULL bestehen. Zu jedem Zeitpunkt kann man einfach eine gültige Datenzeile einfügen, die diese magische Zahl enthält, und plötzlich werden die Dinge schlecht. Es ist im Grunde die Implementierung von Magic Number Anti Pattern. Tu das nicht.
Matthias Hryniszak