Назад JdbcTemplate запитForInt / Long застарілий навесні 3.2.2. Чим його слід замінити?


104

Методи queryforInt / queryforLong у JdbcTemplate застаріли навесні 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 завжди був зручним методом (я думаю) і існує вже давно. Чому його видалено. Код в результаті ускладнюється.



Ви маєте рацію, я не знаю, чому у мого джерела немає@Deprecated
Сотіріос Деліманоліс

Оновлено весняну версію до 3.2.2 - як здається, тут вперше застаріло
Ден Макбій

Я модернізував існуючу базу коду з 3.1 до 3.2.2, і ці методи використовуються всюди. Потрібно зрозуміти, чому і як оновити код.
Ден Макбій

Майте на увазі, що запитForObject може повернутися 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 замість null - для правильного відтворення попередньої поведінки це буде: Integer result = queryForObject (sql, args, Integer.class); повернути результат == null? 0: результат;
MetroidFan2002

@ MetroidFan2002: Дійсно, ваше спостереження вірно! Однак, з точки зору дизайну API, якщо запит просто повертає одне значення NULL, я вважаю, що краще повернути його таким, яким він є, замість того, щоб вважати, що (як queryForInt do) NULL еквівалентний 0. Це завдання користувача API, щоб оцінити такі умови.
Габріель Белінгуер

Проблема полягає в тому, що якщо і коли користувач отримує там NPE, якщо вони явно не встановили певні речі у своєму оточенні (наприклад, у Eclipse є можливість виділити використання автобоксингу), NPE в цьому рядку буде виглядати як екземпляр JDBCOperations є нульовим. Раніше нуль був би повернутий. Тепер, чому б ви використовували це у запиті, який повертає нуль, я не маю уявлення (це, в основному, через n00bs, що вони роблять, що вони будуть робити), але вилучення цих не є чудовим ІМО-ходом.
MetroidFan2002

Я знайшов можливу причину через неточність. У мене було тривале значення 10000000233174211, яке повертається queryForLong (String), але замість цього він повертає 10000000233174212. тобто +1. Я подивився в код, і він перетворює подвійний на довгий, тому, можливо, є певна проблема з перетворенням.
mrswadge

Думаючи про мій коментар вище, тип даних для стовпця був числом (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


13

Заміна такого коду:

long num = jdbcTemplate.queryForLong(sql);

З цим кодом:

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

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

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

Застарілий метод queryForLong у JdbcTemplate навесні 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. Немає можливості відрізнити один випадок від іншого. Я думаю, що це головна причина, чому метод застарілий. BTW, ResultSet#getIntповодиться аналогічно. Хоча ми можемо виділити ці два випадки за 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;
}

Будь ласка, поясніть свою відповідь.
Суворий Вардан
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.