130 votes

Jdbctemplate requête pour la chaîne: EmptyResultDataAccessException: un résultat Incorrect taille: 1, réels 0

Je suis à l'aide de Jdbctemplate pour récupérer une seule valeur de Chaîne à partir de la db. Voici ma méthode.

    public String test() {
        String cert=null;
        String sql = "select ID_NMB_SRZ from codb_owner.TR_LTM_SLS_RTN 
             where id_str_rt = '999' and ID_NMB_SRZ = '60230009999999'";
        cert = (String) jdbc.queryForObject(sql, String.class); 
        return cert;
    }

Dans mon scénario, il est complet possible pour ne PAS en mettre un coup sur ma requête donc ma question est comment puis-je contourner le message d'erreur suivant.

EmptyResultDataAccessException: Incorrect result size: expected 1, actual 0

Il me semble que je viens de récupérer une valeur null au lieu de lancer une exception. Comment puis-je résoudre ce problème? Merci à l'avance.

217voto

Rakesh Juyal Points 8023

Dans JdbcTemplate , queryForInt, queryForLong, queryForObject l'ensemble de ces méthodes s'attend à ce que la requête exécutée sera de retour une et une seule ligne. Si vous n'obtenez pas de lignes ou de plus d'une ligne qui va entraîner IncorrectResultSizeDataAccessException . Maintenant, la façon correcte est de ne pas attraper cette exception ou EmptyResultDataAccessException, mais assurez-vous que la requête que vous utilisez doit retourner une seule ligne. Si il n'est pas possible d'utiliser query méthode à la place.

List<String> strLst  = getJdbcTemplate().query(sql,
            new RowMapper {
public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
        return rs.getString(1);
            }
});


if ( strLst.isEmpty() ){
  return null;
}else if ( strLst.size() == 1 ) { // list contains exactly 1 element
  return strLst.get(0);
}else{  // list contains more than 1 elements
  //your wish, you can either throw the exception or return 1st element.

}

58voto

Brett Ryan Points 4905

Vous pouvez également utiliser un ResultSetExtractor au lieu de RowMapper. Les deux sont tout aussi facile que de l'un à l'autre, la seule différence est que vous gérer l' ResultSet.next().

public String test() {
    String sql = "select ID_NMB_SRZ from codb_owner.TR_LTM_SLS_RTN "
                 + " where id_str_rt = '999' and ID_NMB_SRZ = '60230009999999'";
    return jdbc.query(sql, new ResultSetExtractor<String>() {
        @Override
        public String extractData(ResultSet rs) throws SQLException,
                                                       DataAccessException {
            return rs.next() ? rs.getString("ID_NMB_SRZ") : null;
        }
    });
}

L' ResultSetExtractor a l'avantage supplémentaire que vous pouvez gérer tous les cas où il y a plus que des lignes ou pas de lignes retournées.

Utilisation avec d'autres types est également très simple, la seule réserve étant que vous ne pouvez pas réutiliser le même ResultSetExtractor pour le cas où à la fois une liste et un seul objet doit être retourné.

25voto

Philippe Marschall Points 2439

Ce n'est pas une bonne solution parce que vous êtes en s'appuyant sur les exceptions pour le contrôle de flux. Votre solution c'est normal de se faire des exceptions, il est normal de les avoir dans le journal.

public String test() {
    String sql = "select ID_NMB_SRZ from codb_owner.TR_LTM_SLS_RTN where id_str_rt = '999' and ID_NMB_SRZ = '60230009999999'";
    List<String> certs = jdbc.queryForList(sql, String.class); 
    if (certs.isEmpty()) {
        return null;
    } else {
        return certs.get(0);
    }
}

8voto

satory Points 31

En fait, Vous pouvez jouer avec JdbcTemplate et de personnaliser votre propre méthode que vous préférez. Ma suggestion est de faire quelque chose comme ceci:

public String test() {
    String cert = null;
    String sql = "select ID_NMB_SRZ from codb_owner.TR_LTM_SLS_RTN
        where id_str_rt = '999' and ID_NMB_SRZ = '60230009999999'";
    ArrayList<String> certList = (ArrayList<String>) jdbc.query(
        sql, new RowMapperResultSetExtractor(new UserMapper()));
    cert =  DataAccessUtils.singleResult(certList);

    return cert;
}

Il fonctionne comme l'original, jdbc.queryForObject, mais sans throw new EmptyResultDataAccessException lorsque size == 0.

8voto

Stewart Evans Points 121

Depuis le retour à null quand il n'y a aucune données est quelque chose que je veux faire souvent lors de l'utilisation de queryForObject je trouve qu'il est utile d'étendre JdbcTemplate et ajouter un queryForNullableObject méthode similaire à ci-dessous.

public class JdbcTemplateExtended extends JdbcTemplate {

    public JdbcTemplateExtended(DataSource datasource){
        super(datasource);
    }

    public <T> T queryForNullableObject(String sql, RowMapper<T> rowMapper) throws DataAccessException {
        List<T> results = query(sql, rowMapper);

        if (results == null || results.isEmpty()) {
            return null;
        }
        else if (results.size() > 1) {
            throw new IncorrectResultSizeDataAccessException(1, results.size());
        }
        else{
            return results.iterator().next();
        }
    }

    public <T> T queryForNullableObject(String sql, Class<T> requiredType) throws DataAccessException {
        return queryForObject(sql, getSingleColumnRowMapper(requiredType));
    }

}

Vous pouvez maintenant l'utiliser dans votre code, de la même manière que vous avez utilisé queryForObject

String result = queryForNullableObject(queryString, String.class);

Je serais intéressé de savoir si quelqu'un d'autre pense que c'est une bonne idée?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X