JdbcTemplate queryForInt / Long está obsoleto no Spring 3.2.2. O que deve ser substituído?

104

Os métodos queryforInt / queryforLong em JdbcTemplate estão obsoletos no Spring 3.2. Não consigo descobrir por que ou o que é considerado a prática recomendada para substituir o código existente usando esses métodos.

Um método típico:

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

OK, o método acima precisa ser reescrito da seguinte forma:

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

Obviamente, essa depreciação torna a classe JdbcTemplate mais simples (ou torna?). QueryForInt sempre foi um método de conveniência (eu acho) e já existe há muito tempo. Por que foi removido. O código se torna mais complicado como resultado.

Dan MacBean
fonte
Isso detalha os métodos obsoletos: static.springsource.org/spring/docs/current/javadoc-api/…
Dan MacBean
Você está certo, eu não sei porque minha fonte não tem@Deprecated
Sotirios Delimanolis
Atualizado a versão do Spring para 3.2.2 - como parece que está obsoleto aqui
Dan MacBean
Eu atualizei a base de código existente do 3.1 para o 3.2.2 e esses métodos são usados ​​em todo o lugar. Precisa entender por que e como atualizar o código.
Dan MacBean
Esteja ciente de que queryForObject pode retornar null(não é o caso em seu exemplo). Não encontrei outra maneira senão duplicar agora o código de verificação nulo de queryForInt / Long.
hochraldo

Respostas:

110

O que eu acho é que alguém percebeu que os métodos queryForInt / Long têm semântica confusa, ou seja, a partir do código-fonte JdbcTemplate você pode ver sua implementação atual:

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

o que pode levar você a pensar que, se o conjunto de resultados estiver vazio, ele retornará 0, mas lança uma exceção:

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

portanto, a implementação a seguir é essencialmente equivalente à atual:

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

E então o código não obsoleto agora deve ser substituído pelo feio:

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

ou este (melhor):

    queryForObject(sql, Integer.class, arg1, arg2, ...);
Gabriel Belingueres
fonte
12
Isso não é verdade. O terceiro trecho de código NÃO é igual à implementação! Porque há um NPE oculto com o desempacotamento automático. Se sua consulta retornasse resultados, mas eles fossem nulos, o código anterior retornaria 0 em vez de nulo - para reproduzir corretamente o comportamento anterior, seria: Integer result = queryForObject (sql, args, Integer.class); resultado de retorno == nulo? 0: resultado;
MetroidFan2002
@ MetroidFan2002: De fato, sua observação é verdadeira! No entanto, do ponto de vista do design da API, se a consulta retornar apenas um valor NULL, acredito que seja melhor retorná-lo como está, em vez de assumir que (como queryForInt) um NULL é equivalente a 0. Esse é o trabalho do usuário da API para avaliar esse tipo de condições.
Gabriel Belingueres
O problema é que se e quando um usuário obtiver um NPE lá, a menos que ele defina explicitamente certas coisas em seu ambiente (por exemplo, o Eclipse tem uma opção para destacar os usos de autoboxing), o NPE nessa linha se parecerá com a instância JDBCOperations é nulo. Anteriormente, zero teria sido retornado. Agora, por que você usaria isso em uma consulta que retorna nulo, eu não tenho ideia (isso é basicamente devido a n00bs fazendo isso, o que eles farão), mas tirar isso não é uma grande jogada, IMO.
MetroidFan2002
Eu descobri que uma possível razão é a imprecisão. Eu tinha um valor longo de 10000000233174211 sendo retornado por queryForLong (String), mas em vez disso estava retornando 10000000233174212. ou seja, +1. Eu olhei no código e ele converte um Double em um Long, então talvez haja algum problema com a conversão.
mrswadge
Pensando um pouco mais no meu comentário acima, o tipo de dados para a coluna era número (19,0), então talvez seja por isso que double entrou em jogo? Eu contornei o problema usando queryForObject (sql, Long.class) de qualquer maneira.
Mrswadge
35

Concordo com o autor da postagem original que descontinuar o método de conveniência queryForLong (sql) é um inconveniente.

Eu tinha desenvolvido um aplicativo usando Spring 3.1 e acabei de atualizar para a versão mais recente do Spring (3.2.3) e percebi que estava obsoleto.

Felizmente, foi uma mudança de linha para mim:

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

foi alterado para

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

E alguns testes de unidade parecem indicar que a mudança acima funciona.

SGB
fonte
bom ponto. Funcionaria bem sem o parêntese também. :)
SGB
14

Obsoleto em favor de queryForObject(String, Class).

Verti
fonte
13

Substituindo esse código:

long num = jdbcTemplate.queryForLong(sql);

Com este código:

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

é muito perigoso porque se a coluna tiver valor nulo, queryForObject retornará nulo e, como sabemos, os tipos primitivos não podem ser nulos e você terá NullPointerException. O compilador não avisou sobre isso. Você saberá sobre esse erro em tempo de execução. O mesmo erro que você terá se tiver um método que retorne o tipo primitivo:

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

O método obsoleto queryForLong em JdbcTemplate no Spring 3.2.2 tem o seguinte corpo:

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

Veja, antes de eles retornarem o valor primitivo, verifique se não é nulo e se for nulo, eles retornam 0. A propósito - deve ser 0L.

Marcin Kapusta
fonte
3
2 centavos: O compilador pode avisá-lo sobre isso, se você habilitou o aviso de autoboxing.
keiki
Eu não sabia disso. Obrigado amigo :)
Marcin Kapusta
2

JdbcTemplate#queryForIntretorna 0 se o valor da coluna for SQL NULL ou 0. Não há como distinguir um caso do outro. Acho que essa é a principal razão pela qual o método está obsoleto. BTW, ResultSet#getIntse comporta de forma semelhante. Porém, podemos distinguir entre esses dois casos por ResultSet#wasNull.

jddxf
fonte
-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
fonte
Explique sua resposta.
Harsh Wardhan