Recherche d'une valeur int nulle à partir d'un ensemble de résultats Java

277

En Java, j'essaie de tester une valeur nulle, à partir d'un ResultSet, où la colonne est convertie en un type int primitif .

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

D'après le fragment de code ci-dessus, existe-t-il une meilleure façon de le faire, et je suppose que le deuxième test wasNull () est redondant?

Renseignez-nous et merci

ian_scho
la source
14
J'ai trouvé cette question car j'ai une colonne nullable dans une base de données et elle est représentée par un entier en Java. On pourrait penser qu'avoir une colonne numérique nullable dans une base de données serait suffisamment courant pour que l'API ResultSet puisse l'adapter un peu plus élégamment.
spaaarky21
Je ne poste pas cela comme une réponse car elle est tangentielle et loin d'être universelle: ma solution habituelle à cela est de mettre IF(colName = NULL, 0, colName) AS colNamedans la SELECTdéclaration (de préférence dans un proc stocké). D'un point de vue philosophique, cela revient à déterminer si la base de données doit être conforme à l'application, ou vice versa. Étant donné que SQL gère facilement les valeurs NULL et que de nombreux consommateurs SQL ne le font pas (c'est-à-dire java.sql.ResultSet), je choisis de le gérer au niveau de la base de données lorsque cela est possible. (Ceci, bien sûr, suppose que conceptuellement NULL et zéro sont équivalents pour vos besoins.)
s.co.tt

Réponses:

368

La valeur par défaut ResultSet.getIntlorsque la valeur du champ NULLest de retourner 0, qui est également la valeur par défaut de votre iValdéclaration. Dans ce cas, votre test est complètement redondant.

Si vous voulez réellement faire quelque chose de différent si la valeur du champ est NULL, je suggère:

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

(Modifié sous les commentaires @martin ci-dessous; le code OP tel qu'il est écrit ne se compilera pas car il iValn'est pas initialisé)

Richard
la source
2
Je viens de trouver la même déclaration dans les documents. Cela vaut un fil séparé sur SO à mon humble avis. ( java.sun.com/j2se/1.4.2/docs/api/java/sql/… )
Roman
6
@Roman - voir le javadoc pour getInt dans ResultSet: "Renvoie: la valeur de la colonne; si la valeur est SQL NULL, la valeur renvoyée est 0"
Cowan
150
La vérité est, en effet, ridicule. getInt()devrait être getInteger()ce qui renvoie un Integerqui est nullsi la valeur DB est null. Les développeurs ont vraiment foiré celui-ci.
ryvantage
7
@sactiw suivant cette logique, tout sur java aurait dû être développé pour éviter NPE, ce qui n'est pas le cas. Éviter NPE est la responsabilité des développeurs d'applications, pas des API internes du langage.
Mateus Viccari
10
OMG Pourquoi, ne renvoie tout simplement pas NULL, 0et ce NULLsont deux choses très différentes
deFreitas
84

Une autre solution:

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
la source
27

Je pense que c'est redondant. rs.getObject("ID_PARENT")doit renvoyer un Integerobjet ou null, si la valeur de la colonne était réellement NULL. Il devrait donc même être possible de faire quelque chose comme:

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
la source
5
Hm, au moins dans mon cas, le problème avec cela est que l'appel getObjectne retourne pas nécessairement un entier, en raison de la nature du type de colonne dans la base de données Oracle que j'utilise ("Number").
Matt Mc
Même problème avec Matt ... Avec MySQL et Types.BIGINT(qui devrait être mappé sur a Long), la getObject()méthode retourne 0 au lieu de null.
xonya
2
Pourrait aussi fairers.getObject("ID_PARENT", Integer.class)
Arlo
26

Vérifiez simplement si le champ nullutilise ou non ResultSet#getObject(). Remplacez -1par la valeur de casse nulle souhaitée.

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

Ou, si vous pouvez garantir que vous utilisez le bon type de colonne de base de données de manière à ce ResultSet#getObject()qu'il renvoie vraiment un Integer(et donc pas Long, Shortou Byte), vous pouvez également le transtyper en un Integer.

Integer foo = (Integer) resultSet.getObject("foo");
BalusC
la source
1
Non, mais il construira un objet Integer inutile dans le cas non nul. (Et BTW la plupart des pilotes JDBC n'atteignent pas la base de données pendant les appels à la méthode ResultSet ... en général, vous ne récupérez le ResultSet que lorsque toutes les données sont passées par le câble).
EricS
Cela dépend de la taille de l'extraction. Dans la plupart des pilotes, la valeur par défaut est de 10 lignes et une fois l'extraction récupérée, elles seront traitées, mais l'extraction suivante ne sera pas récupérée avant la fin du traitement.
Kristo Aun
Je suis surpris que votre réponse ne soit pas la meilleure réponse :-) Je vote parce que c'est la bonne réponse qui évite la méthode wasNull () très délicate et dangereuse. Pour moi, c'est une raison supplémentaire d'arrêter d'utiliser Java ;-) et de continuer à utiliser VB.Net où RecordSet résout ce problème facile depuis plus de 10 ans!
schlebe
11

AFAIK, vous pouvez simplement utiliser

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

même si c'est NULL.

Peter Tillemans
la source
5

Juste une mise à jour avec Java Generics.

Vous pouvez créer une méthode utilitaire pour récupérer une valeur facultative de tout type Java à partir d'un ResultSet donné, préalablement casté.

Malheureusement, getObject (columnName, Class) ne renvoie pas null, mais la valeur par défaut pour le type Java donné, donc 2 appels sont requis

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

Dans cet exemple, votre code pourrait ressembler à ci-dessous:

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

Heureux d'avoir des commentaires

luchoct
la source
2

Vous pouvez appeler cette méthode à l'aide de resultSet et du nom de colonne de type Number. Il retournera soit la valeur entière, soit null. Aucun zéro ne sera retourné pour la valeur vide dans la base de données

private Integer getIntWithNullCheck(ResultSet rset, String columnName) {
    try {
        Integer value = rset.getInt(columnName);
        return rset.wasNull() ? null : value;
    } catch (Exception e) {
        return null;
    }
}
amine kriaa
la source
Pouvez-vous s'il vous plaît entrer plus en détail sur la façon dont cela résout la question?
Sterling Archer
Vous pouvez appeler cette méthode à l'aide de resultSet et du nom de colonne de type Number. Il retournera soit la valeur entière, soit null. Aucun zéro ne sera retourné pour la valeur vide dans la base de données.
amine kriaa
Excellent! Modifiez cela dans votre réponse (et supprimez le commentaire après la modification) et vous avez vous-même une bonne réponse :)
Sterling Archer
1

Avec java 8, vous pouvez faire ceci:

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

Dans ce cas, vous vous assurez que le nVal sera nul (et non nul) si la valeur SQL est NULL

George
la source
2
mais ne s'applique pas àresultSet.getInt("col_name")
ravi
1
Avec la source de données MSSQL, cela ne semble pas fonctionner. Il doit avoir le contrôle supplémentaire deif (rs.wasNull())
alltej
1

Pour plus de commodité, vous pouvez créer une classe wrapper autour de ResultSet qui renvoie des valeurs nulles alors que ResultSetce n'est généralement pas le cas.

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
la source
-6

Une autre bonne façon de vérifier, si vous avez le contrôle du SQL, est d'ajouter une valeur par défaut dans la requête elle-même pour votre colonne int. Vérifiez ensuite cette valeur.

par exemple pour une base de données Oracle, utilisez NVL

SELECT NVL(ID_PARENT, -999) FROM TABLE_NAME;

puis vérifier

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

Bien sûr, cela est également sous l'hypothèse qu'il existe une valeur qui ne serait normalement pas trouvée dans la colonne.

Chris
la source
12
J'ai voté contre cette réponse car elle est très susceptible de causer des problèmes à beaucoup de gens. Une colonne int si définie comme nullable a un ensemble de valeurs composé de nombres positifs, zéro, nombres négatifs et NULL. À tout moment, on peut simplement insérer une ligne de données valide contenant ce nombre magique et toutes les choses soudaines iront mal. Il s'agit essentiellement de la mise en œuvre d'un modèle anti-nombre magique. Ne fais pas ça.
Matthias Hryniszak