JdbcTemplate queryForInt / Long устарел в Spring 3.2.2. Чем его заменить?

104

Методы queryforInt / queryforLong в JdbcTemplate устарели в Spring 3.2. Я не могу понять, почему или что считается лучшей практикой для замены существующего кода с помощью этих методов.

Типичный метод:

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

ОК, приведенный выше метод необходимо переписать следующим образом:

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

Очевидно, что это устаревание упрощает класс JdbcTemplate (или делает?). QueryForInt всегда был удобным методом (я думаю) и существует уже давно. Почему его удалили. В результате код становится более сложным.

Дэн МакБин
источник
Здесь подробно описаны устаревшие методы: static.springsource.org/spring/docs/current/javadoc-api/…
Дэн МакБин,
Вы правы, я не знаю, почему в моем источнике нет@Deprecated
Сотириос Делиманолис
Обновил версию Spring до 3.2.2 - как кажется, она сначала устарела здесь
Дэн Макбин
Я обновил существующую кодовую базу с 3.1 до 3.2.2, и эти методы используются повсеместно. Нужно понять, зачем и как обновлять код.
Dan MacBean
Имейте в виду, что queryForObject может возвращать null(не в вашем примере). Я не нашел другого способа, кроме как продублировать код нулевой проверки из queryForInt / Long.
hochraldo

Ответы:

110

Я думаю, что кто-то понял, что методы queryForInt / Long имеют запутанную семантику, то есть из исходного кода JdbcTemplate вы можете увидеть его текущую реализацию:

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

что может привести вас к мысли, что если набор результатов пуст, он вернет 0, однако выдает исключение:

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

так что следующая реализация по существу эквивалентна текущей:

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

И тогда не устаревший код теперь нужно заменить уродливым:

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

или это (лучше):

    queryForObject(sql, Integer.class, arg1, arg2, ...);
Габриэль Белингерес
источник
12
Это неправда. Третий фрагмент кода НЕ равен реализации! Потому что есть скрытый NPE с автоматической распаковкой. Если ваш запрос вернул результаты, но они были пустыми, предыдущий код вернул бы 0 вместо нуля - чтобы правильно воспроизвести предыдущее поведение, это будет: Integer result = queryForObject (sql, args, Integer.class); вернуть результат == null? 0: результат;
MetroidFan2002 05
@ MetroidFan2002: Действительно, ваше наблюдение верно! Однако с точки зрения дизайна API, если запрос просто возвращает одно значение NULL, я считаю, что лучше вернуть его как есть, а не считать, что (как это делает queryForInt) NULL эквивалентно 0. Это работа. пользователя API, чтобы оценить такие условия.
Габриэль Белингерес
Проблема в том, что если и когда пользователь получает там NPE, если он явно не установил определенные вещи в своей среде (например, Eclipse имеет возможность выделять использование автобокса), NPE в этой строке будет выглядеть как экземпляр JDBCOperations нулевой. Раньше возвращался бы ноль. Теперь, почему вы должны использовать это в запросе, который возвращает null, я понятия не имею (это в основном из-за того, что n00bs делают это, что они и будут), но убрать это - не лучший шаг IMO.
MetroidFan2002 06
Я нашел возможную причину из-за неточности. У меня было длинное значение 10000000233174211, возвращаемое queryForLong (String), но вместо этого оно возвращало 10000000233174212. т.е. +1. Я просмотрел код, и он преобразует Double в Long, поэтому, возможно, есть какая-то проблема с преобразованием.
mrswadge 06
Если подумать о моем комментарии выше, тип данных для столбца был числом (19,0), так что, может быть, поэтому двойное значение вошло в игру? Я все равно обошел проблему, используя queryForObject (sql, Long.class).
mrswadge
35

Я согласен с исходным постером, что отказ от удобного метода queryForLong (sql) является неудобством.

Я разработал приложение с использованием Spring 3.1 и только что обновил его до последней версии Spring (3.2.3) и заметил, что оно устарело.

К счастью, для меня это было изменение одной строчки:

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

был изменен на

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

И пара модульных тестов, похоже, указывает на то, что вышеуказанное изменение работает.

SGB
источник
хорошая точка зрения. Было бы хорошо работать и без скобок. :)
SGB
14

Устарело в пользу queryForObject(String, Class).

вертти
источник
13

Замена такого кода:

long num = jdbcTemplate.queryForLong(sql);

С этим кодом:

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

очень опасно, потому что если столбец имеет нулевое значение, queryForObject возвращает null, и, как мы знаем, примитивные типы не могут быть нулевыми, и у вас будет NullPointerException. Компилятор Вас об этом не предупреждал. Вы узнаете об этой ошибке во время выполнения. Та же ошибка, что и у вас, если у вас есть метод, возвращающий примитивный тип:

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

Устаревший метод queryForLong в JdbcTemplate в Spring 3.2.2 имеет следующее тело:

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

Видите ли, прежде чем они вернут примитивное значение, проверяется, не является ли оно нулевым, и если оно равно нулю, они возвращают 0. Между прочим - должно быть 0L.

Марчин Капуста
источник
3
2 цента: компилятор может вас предупредить об этом, если вы включили предупреждение автобокса.
keiki
Я не знал об этом. Спасибо, дружище :)
Марцин Капуста
2

JdbcTemplate#queryForIntвозвращает 0, если значение столбца - SQL NULL или 0. Невозможно отличить один регистр от другого. Я думаю, что это основная причина, по которой метод устарел. Кстати, ResultSet#getIntведет себя аналогично. Хотя мы можем различить эти два случая по ResultSet#wasNull.

jddxf
источник
-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;
}
Маниканнан Арумугам
источник
Пожалуйста, объясните свой ответ.
Harsh Wardhan 05