JdbcTemplate queryForInt / Long está en desuso en Spring 3.2.2. ¿Por qué debería ser reemplazado?

104

Los métodos queryforInt / queryforLong en JdbcTemplate están obsoletos en Spring 3.2. No puedo averiguar por qué o qué se considera la mejor práctica para reemplazar el código existente utilizando estos métodos.

Un método típico:

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

OK, el método anterior debe reescribirse de la siguiente manera:

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

Obviamente, esta desaprobación hace que la clase JdbcTemplate sea más simple (¿o no?). QueryForInt siempre fue un método de conveniencia (supongo) y ha existido durante mucho tiempo. ¿Por qué se ha eliminado? Como resultado, el código se vuelve más complicado.

Dan MacBean
fuente
Esto detalla los métodos obsoletos: static.springsource.org/spring/docs/current/javadoc-api/…
Dan MacBean
Tienes razón, no sé por qué mi fuente no tiene@Deprecated
Sotirios Delimanolis
Se actualizó la versión Spring a 3.2.2, ya que parece que está obsoleta por primera vez aquí
Dan MacBean
Actualicé la base de código existente de 3.1 a 3.2.2 y estos métodos se utilizan en todas partes. Necesito entender por qué y cómo actualizar el código.
Dan MacBean
Tenga en cuenta que queryForObject podría devolver null(no es el caso en su ejemplo). No encontré otra forma que duplicar ahora el código de verificación nulo de queryForInt / Long.
hochraldo

Respuestas:

110

Lo que creo es que alguien se dio cuenta de que los métodos queryForInt / Long tienen semánticas confusas, es decir, desde el código fuente de JdbcTemplate se puede ver su implementación actual:

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

lo que puede llevarlo a pensar que si el conjunto de resultados está vacío, devolverá 0, sin embargo, arroja una excepción:

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

por lo que la siguiente implementación es esencialmente equivalente a la actual:

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

Y luego, el código no obsoleto ahora debe reemplazarse con el feo:

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

o esto (mejor):

    queryForObject(sql, Integer.class, arg1, arg2, ...);
Gabriel Belingueres
fuente
12
Eso no es cierto. ¡El tercer fragmento de código NO es igual a la implementación! Porque hay una NPE oculta con unboxing automático. Si su consulta arrojó resultados, pero eran nulos, el código anterior devolvería 0 en lugar de nulo; para reproducir correctamente el comportamiento anterior, sería: Integer result = queryForObject (sql, args, Integer.class); devolver resultado == nulo? 0: resultado;
MetroidFan2002
@ MetroidFan2002: ¡De hecho, su observación es cierta! Sin embargo, desde el punto de vista del diseño de la API, si la consulta solo devuelve un valor NULL, creo que es mejor devolverlo como está, en lugar de asumir que (como queryForInt) un NULL es equivalente a 0. Es el trabajo del usuario de la API para evaluar ese tipo de condiciones.
Gabriel Belingueres
El problema es que si y cuando un usuario obtiene un NPE allí, a menos que establezcan explícitamente ciertas cosas en su entorno (por ejemplo, Eclipse tiene una opción para resaltar los usos del autoboxing), el NPE en esa línea se verá como la instancia de JDBCOperations. es nulo. Anteriormente, se habría devuelto cero. Ahora, ¿por qué usaría esto en una consulta que devuelve nulo? No tengo idea (esto se debe básicamente a que n00bs lo hacen, lo que harán), pero eliminarlos no es un gran movimiento en mi opinión.
MetroidFan2002
Encontré que una posible razón es la inexactitud. Tenía un valor largo de 10000000233174211 devuelto por queryForLong (String), pero en su lugar estaba devolviendo 10000000233174212. es decir, +1. Miré en el código y convierte un Double en un Long, por lo que quizás haya algún problema con la conversión.
mrswadge
Pensando un poco más en mi comentario anterior, el tipo de datos para la columna era número (19,0), así que tal vez por eso entró en juego el doble. De todos modos, solucioné el problema usando queryForObject (sql, Long.class).
mrswadge
35

Estoy de acuerdo con el póster original de que desaprobar el método de conveniencia queryForLong (sql) es un inconveniente.

Desarrollé una aplicación con Spring 3.1 y la actualicé a la última versión de Spring (3.2.3) y noté que estaba obsoleta.

Afortunadamente, fue un cambio de una línea para mí:

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

fue cambiado a

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

Y un par de pruebas unitarias parecen indicar que el cambio anterior funciona.

SGB
fuente
buen punto. También funcionaría bien sin el paréntesis. :)
SGB
14

En desuso a favor de queryForObject(String, Class).

vertti
fuente
13

Reemplazo de dicho código:

long num = jdbcTemplate.queryForLong(sql);

Con este código:

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

es muy peligroso porque si la columna tiene un valor nulo, queryForObject devuelve un valor nulo y, como sabemos, los tipos primitivos no pueden ser nulos y tendrá NullPointerException. El compilador no le advirtió sobre esto. Sabrá acerca de este error en tiempo de ejecución. El mismo error que tendrá si tiene un método que devuelve el tipo primitivo:

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

El método obsoleto queryForLong en JdbcTemplate en Spring 3.2.2 tiene el siguiente cuerpo:

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

Verá, antes de que devuelvan un valor primitivo, hay que verificar que no sea nulo y, si es nulo, devuelvan 0. Por cierto, debería ser 0L.

Marcin Kapusta
fuente
3
2 centavos: el compilador puede advertirle sobre esto, si habilitó la advertencia de autoboxing.
keiki
Yo no sabía nada de eso. Gracias amigo :)
Marcin Kapusta
2

JdbcTemplate#queryForIntdevuelve 0 si el valor de la columna es SQL NULL o 0. No hay forma de distinguir un caso del otro. Creo que esta es la razón principal por la que el método está en desuso. Por cierto, se ResultSet#getIntcomporta de manera similar. Sin embargo, podemos distinguir entre estos dos casos por ResultSet#wasNull.

jddxf
fuente
-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
fuente
Por favor explique su respuesta.
Harsh Wardhan