JdbcTemplate queryForInt / Long è deprecato nella primavera 3.2.2. Da cosa dovrebbe essere sostituito?


104

I metodi queryforInt / queryforLong in JdbcTemplate sono deprecati nella Spring 3.2. Non riesco a scoprire perché o quale sia considerata la migliore pratica per sostituire il codice esistente utilizzando questi metodi.

Un metodo tipico:

int rowCount = jscoreJdbcTemplate.queryForInt(
    "SELECT count(*) FROM _player WHERE nameKey = ? AND teamClub = ?",
    playerNameKey.toUpperCase(),
    teamNameKey.toUpperCase()
);

OK, il metodo sopra deve essere riscritto come segue:

Object[] params = new Object[] { 
   playerNameKey.toUpperCase(), 
   teamNameKey.toUpperCase()
};
int rowCount = jscoreJdbcTemplate.queryForObject(
    "SELECT count(*) FROM _player WHERE nameKey = ? AND teamClub = ?",
    params, Integer.class);

Ovviamente questa deprecazione rende la classe JdbcTemplate più semplice (o lo fa?). QueryForInt è sempre stato un metodo conveniente (immagino) ed è in circolazione da molto tempo. Perché è stato rimosso. Di conseguenza, il codice diventa più complicato.



Hai ragione, non so perché la mia fonte non ha@Deprecated
Sotirios Delimanolis

Aggiornata la versione primaverile alla 3.2.2 - poiché sembra essere deprecata per la prima volta qui
Dan MacBean

Ho aggiornato la base di codice esistente da 3.1 a 3.2.2 e questi metodi sono usati ovunque. Hai bisogno di capire perché e come aggiornare il codice.
Dan MacBean

Tieni presente che queryForObject potrebbe restituire null(non è il caso nel tuo esempio). Non ho trovato altro modo che duplicare ora il codice di controllo null da queryForInt / Long.
hochraldo

Risposte:


110

Quello che penso è che qualcuno si sia reso conto che i metodi queryForInt / Long hanno una semantica confusa, cioè dal codice sorgente JdbcTemplate puoi vedere la sua attuale implementazione:

@Deprecated
public int queryForInt(String sql, Object... args) throws DataAccessException {
    Number number = queryForObject(sql, args, Integer.class);
    return (number != null ? number.intValue() : 0);
}

il che potrebbe farti pensare che se il set di risultati è vuoto restituirà 0, tuttavia genera un'eccezione:

org.springframework.dao.EmptyResultDataAccessException: Incorrect result size: expected 1, actual 0

quindi la seguente implementazione è essenzialmente equivalente a quella attuale:

@Deprecated
public int queryForInt(String sql, Object... args) throws DataAccessException {
    return queryForObject(sql, args, Integer.class);
}

E poi il codice non deprecato ora deve essere sostituito con il brutto:

    queryForObject(sql, new Object { arg1, arg2, ...}, Integer.class);

o questo (più bello):

    queryForObject(sql, Integer.class, arg1, arg2, ...);

12
Non è vero. Il terzo frammento di codice NON è uguale all'implementazione! Perché c'è un NPE nascosto con l'unboxing automatico. Se la query ha restituito risultati, ma erano nulli, il codice precedente restituirebbe 0 invece di nullo - per riprodurre correttamente il comportamento precedente, sarebbe: Integer result = queryForObject (sql, args, Integer.class); risultato di ritorno == null? 0: risultato;
MetroidFan2002

@ MetroidFan2002: in effetti la tua osservazione è vera! Tuttavia, dal punto di vista della progettazione dell'API, se la query restituisce solo un valore NULL, credo sia meglio restituirlo così com'è, invece di sommare che (come fa queryForInt) un NULL è equivalente a 0. È il lavoro dell'utente API per valutare quel tipo di condizioni.
Gabriel Belingueres

Il problema è che se e quando un utente ottiene un NPE lì, a meno che non abbia impostato esplicitamente determinate cose nel proprio ambiente (ad esempio, Eclipse ha un'opzione per evidenziare gli usi dell'autoboxing), l'NPE su quella linea apparirà come l'istanza JDBCOperations è zero. In precedenza, sarebbe stato restituito zero. Ora perché dovresti usarlo in una query che restituisce null, non ne ho idea (questo è fondamentalmente dovuto al fatto che n00bs lo fa, cosa che lo faranno), ma portarli via non è una grande mossa IMO.
MetroidFan2002

Ho trovato una possibile ragione a causa di inesattezza. Avevo un valore lungo di 10000000233174211 restituito da queryForLong (String), ma invece restituiva 10000000233174212. cioè +1. Ho guardato nel codice e converte un Double in un Long, quindi forse c'è qualche problema con la conversione.
mrswadge

Ripensando un po 'più al mio commento sopra, il tipo di dati per la colonna era il numero (19,0), quindi forse è per questo che è entrato in gioco il doppio? Ho aggirato il problema utilizzando comunque queryForObject (sql, Long.class).
mrswadge

35

Sono d'accordo con il poster originale che deprecare il metodo di convenienza queryForLong (sql) è un inconveniente.

Avevo sviluppato un'app utilizzando Spring 3.1 e appena aggiornato all'ultima versione Spring (3.2.3) e ho notato che era deprecata.

Fortunatamente, per me è stato un cambio di riga:

return jdbcTemplate.queryForLong(sql);  // deprecated in Spring 3.2.x

è stato cambiato in

return jdbcTemplate.queryForObject(sql, Long.class);

E un paio di test unitari sembrano indicare che la modifica di cui sopra funziona.


buon punto. Funzionerebbe bene anche senza le parentesi. :)
SGB

14

Deprecato a favore di queryForObject(String, Class).


13

Sostituzione di tale codice:

long num = jdbcTemplate.queryForLong(sql);

Con questo codice:

long num = jdbcTemplate.queryForObject(sql, Long.class);

è molto pericoloso perché se la colonna ha valore nullo queryForObject restituisce nullo e come sappiamo i tipi primitivi non possono essere nulli e avrai NullPointerException. Il compilatore non ti ha avvertito di questo. Saprai di questo errore in fase di esecuzione. Lo stesso errore che avrai se hai un metodo che restituisce il tipo primitivo:

public long getValue(String sql) {
    return = jdbcTemplate.queryForObject(sql, Long.class);
}

Il metodo deprecato queryForLong in JdbcTemplate in Spring 3.2.2 ha il corpo seguente:

@Deprecated
public long queryForLong(String sql) throws DataAccessException {
    Number number = queryForObject(sql, Long.class);
    return (number != null ? number.longValue() : 0);
}

Vedete, prima che restituiscano il valore primitivo, è necessario verificare che questo non sia nullo e se è nullo restituiscono 0. A proposito - Dovrebbe essere 0L.


3
2 centesimi: il compilatore potrebbe avvisarti se hai abilitato l'avviso di box automatico.
keiki

Non lo sapevo. Grazie
amico

2

JdbcTemplate#queryForIntrestituisce 0 se il valore della colonna è SQL NULL o 0. Non è possibile distinguere un caso dall'altro. Penso che questo sia il motivo principale per cui il metodo è deprecato. BTW, ResultSet#getIntsi comporta in modo simile. Tuttavia, possiamo distinguere tra questi due casi da ResultSet#wasNull.


-1
public int getCircleCount() {
    Object param = "1";
    String sql = "select count(*) from circle where id = ? ";
    jdbcTemplate.setDataSource(getDataSource());
    int result = getJdbcTemplate().queryForObject(sql, new Object[] { param }, Integer.class);
    return result;
}

Per favore, spiega la tua risposta.
Harsh Wardhan
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.