JdbcTemplate queryForInt / Long jest przestarzała w Spring 3.2.2. Czym należy go zastąpić?

104

Metody queryforInt / queryforLong w JdbcTemplate są przestarzałe w wersji Spring 3.2. Nie mogę dowiedzieć się, dlaczego lub co jest uważane za najlepszą praktykę zastępowania istniejącego kodu przy użyciu tych metod.

Typowa metoda:

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

OK powyższą metodę należy przepisać w następujący sposób:

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

Oczywiście to wycofanie sprawia, że ​​klasa JdbcTemplate jest prostsza (czy też robi?). QueryForInt zawsze była wygodną metodą (chyba) i istnieje od dawna. Dlaczego został usunięty. W rezultacie kod staje się bardziej skomplikowany.

Dan MacBean
źródło
Szczegóły dotyczące przestarzałych metod: static.springsource.org/spring/docs/current/javadoc-api/ ...
Dan MacBean
Masz rację, nie wiem, dlaczego moje źródło nie ma@Deprecated
Sotirios Delimanolis
Zaktualizowałem wersję wiosenną do 3.2.2 - jak się wydaje, została tutaj wycofana
Dan MacBean
Zaktualizowałem istniejący kod z 3.1 do 3.2.2 i te metody są używane wszędzie. Musisz zrozumieć, dlaczego i jak zaktualizować kod.
Dan MacBean
Należy pamiętać, że queryForObject może zwrócić null(nie ma to miejsca w Twoim przykładzie). Nie znalazłem innego sposobu niż zduplikowanie teraz zerowego kodu kontrolnego z queryForInt / Long.
hochraldo

Odpowiedzi:

110

Myślę, że ktoś zdał sobie sprawę, że metody queryForInt / Long mają mylącą semantykę, to znaczy z kodu źródłowego JdbcTemplate widać jego aktualną implementację:

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

co może sprawić, że pomyślisz, że jeśli zestaw wyników jest pusty, zwróci 0, jednak zgłasza wyjątek:

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

więc następująca implementacja jest zasadniczo równoważna z obecną:

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

A potem nieaktualny kod musi teraz zostać zastąpiony brzydkim:

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

lub to (ładniej):

    queryForObject(sql, Integer.class, arg1, arg2, ...);
Gabriel Belingueres
źródło
12
To nieprawda. Trzeci fragment kodu NIE jest równy implementacji! Ponieważ istnieje ukryty NPE z automatycznym rozpakowywaniem. Jeśli zapytanie zwróciło wyniki, ale były one zerowe, poprzedni kod zwróciłby 0 zamiast null - aby poprawnie odtworzyć poprzednie zachowanie, byłoby to: Integer result = queryForObject (sql, args, Integer.class); zwracany wynik == null? 0: wynik;
MetroidFan2002,
@ MetroidFan2002: Rzeczywiście, twoja obserwacja jest prawdziwa! Jednak z punktu widzenia projektu API, jeśli zapytanie zwraca tylko jedną wartość NULL, uważam, że lepiej jest zwrócić ją w takiej postaci, w jakiej jest, zamiast zakładać, że (jak to robi queryForInt), wartość NULL jest równa 0. To jest zadanie użytkownika API do oceny tego rodzaju warunków.
Gabriel Belingueres
Problem polega na tym, że jeśli i kiedy użytkownik dostanie tam NPE, chyba że wyraźnie ustawi pewne rzeczy w swoim środowisku (na przykład Eclipse ma opcję wyróżnienia zastosowań autoboxingu), NPE w tej linii będzie wyglądać jak instancja JDBCOperations jest null. Wcześniej zwracane byłoby zero. Dlaczego miałbyś użyć tego w zapytaniu, które zwraca wartość null, nie mam pojęcia (jest to zasadniczo spowodowane tym, że n00bs to robi, co zrobią), ale usunięcie ich nie jest wielkim ruchem IMO.
MetroidFan2002,
Znalazłem możliwą przyczynę z powodu niedokładności. Miałem długą wartość 10000000233174211 zwracaną przez queryForLong (String), ale zamiast tego zwracała 10000000233174212, tj. +1. Zajrzałem do kodu i konwertuje on Double na Long, więc być może jest jakiś problem z konwersją.
mrswadge
Myśląc nieco dalej o moim komentarzu powyżej, typem danych dla kolumny była liczba (19,0), więc może dlatego w grę wchodziło double? W każdym razie rozwiązałem ten problem, używając queryForObject (sql, Long.class).
mrswadge
35

Zgadzam się z oryginalnym postem, że rezygnacja z wygodnej metody queryForLong (sql) jest niedogodnością.

Napisałem aplikację przy użyciu Spring 3.1 i właśnie zaktualizowałem do najnowszej wersji Spring (3.2.3) i zauważyłem, że jest przestarzała.

Na szczęście była to dla mnie zmiana w jednym wierszu:

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

został zmieniony na

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

Kilka testów jednostkowych wydaje się wskazywać, że powyższa zmiana działa.

SGB
źródło
Słuszna uwaga. Bez nawiasów też by działało. :)
SGB
14

Przestarzałe na rzecz queryForObject(String, Class).

vertti
źródło
13

Zastąpienie takiego kodu:

long num = jdbcTemplate.queryForLong(sql);

Za pomocą tego kodu:

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

jest bardzo niebezpieczne, ponieważ jeśli kolumna ma wartość null, queryForObject zwraca wartość null, a jak wiemy, typy pierwotne nie mogą być puste i otrzymasz NullPointerException. Kompilator Cię o tym nie ostrzegł. Dowiesz się o tym błędzie w czasie wykonywania. Ten sam błąd, który będziesz miał, jeśli masz metodę zwracającą typ pierwotny:

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

Przestarzała metoda queryForLong w JdbcTemplate w Spring 3.2.2 ma następującą treść:

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

Widzisz, zanim zwrócą wartość pierwotną, sprawdź, czy nie jest to null, a jeśli jest zerowe, zwracają 0. Nawiasem mówiąc - powinno być 0L.

Marcin Kapusta
źródło
3
2 centy: Kompilator może Cię o tym ostrzec, jeśli włączyłeś ostrzeżenie o automatycznym blokowaniu.
keiki
Nie wiedziałem o tym. Dzięki kolego :)
Marcin Kapusta
2

JdbcTemplate#queryForIntzwraca 0, jeśli wartością kolumny jest SQL NULL lub 0. Nie ma sposobu, aby odróżnić jedną wielkość liter od drugiej. Myślę, że jest to główny powód, dla którego metoda jest przestarzała. BTW, ResultSet#getIntzachowuje się podobnie. Chociaż możemy rozróżnić te dwa przypadki według ResultSet#wasNull.

jddxf
źródło
-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;
}
Manikannan Arumugam
źródło
Proszę wyjaśnij swoją odpowiedź.
Harsh Wardhan