Eu tenho um método para obter usuários de um banco de dados com JDBC:
public List<User> getUser(int userId) {
String sql = "SELECT id, name FROM users WHERE id = ?";
List<User> users = new ArrayList<User>();
try {
Connection con = DriverManager.getConnection(myConnectionURL);
PreparedStatement ps = con.prepareStatement(sql);
ps.setInt(1, userId);
ResultSet rs = ps.executeQuery();
while(rs.next()) {
users.add(new User(rs.getInt("id"), rs.getString("name")));
}
rs.close();
ps.close();
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
return users;
}
Como devo usar o Java 7 try-with-resources para melhorar esse código?
Eu tentei com o código abaixo, mas ele usa muitos try
blocos e não melhora muito a legibilidade . Devo usar try-with-resources
de outra maneira?
public List<User> getUser(int userId) {
String sql = "SELECT id, name FROM users WHERE id = ?";
List<User> users = new ArrayList<>();
try {
try (Connection con = DriverManager.getConnection(myConnectionURL);
PreparedStatement ps = con.prepareStatement(sql);) {
ps.setInt(1, userId);
try (ResultSet rs = ps.executeQuery();) {
while(rs.next()) {
users.add(new User(rs.getInt("id"), rs.getString("name")));
}
}
}
} catch (SQLException e) {
e.printStackTrace();
}
return users;
}
java
jdbc
java-7
try-with-resources
Jonas
fonte
fonte
try (ResultSet rs = ps.executeQuery()) {
, porque Um ResultSet objeto é automaticamente fechada pelo objeto Statement que o gerouConnection
,PreparedStatement
eResultSet
também. Não há razão para não fazer isso, pois a tentativa com recursos facilita e torna nosso código mais auto-documentado quanto às nossas intenções.Respostas:
Não há necessidade de uma tentativa externa no seu exemplo; portanto, você pode pelo menos descer de 3 para 2 e também não precisa fechar
;
no final da lista de recursos. A vantagem de usar dois blocos try é que todo o seu código está presente antecipadamente, assim você não precisa se referir a um método separado:fonte
Connection::setAutoCommit
? Essa chamada não é permitida entre otry
entrecon =
eps =
. Ao obter uma conexão de um DataSource que pode ser apoiada por um conjunto de conexões, não podemos assumir como o autoCommit está definido.DriverManager.getConnection(myConnectionURL)
para um método que também define o sinalizador autoCommit e retorna a conexão (ou colocá-lo no equivalente docreatePreparedStatement
método no exemplo anterior ...)DataSource
onde ogetConnection
método faz o que você diz, obtenha a conexão e configure-a conforme necessário, passando a conexão.Sei que isso foi respondido há muito tempo, mas quero sugerir uma abordagem adicional que evite o bloco duplo aninhado try-with-resources.
fonte
createPreparedStatement
não é seguro, independentemente de como você o use. Para corrigi-lo, você teria que adicionar um try-catch ao redor do setInt (...), pegar qualquer umSQLException
e, quando isso acontecer, chamar ps.close () e repetir a exceção. Mas isso resultaria em um código quase tão longo e deselegante quanto o código que o OP queria melhorar.Aqui está uma maneira concisa de usar lambdas e JDK 8 Supplier para ajustar tudo na tentativa externa:
fonte
Que tal criar uma classe adicional de wrapper?
Em seguida, na classe de chamada, você pode implementar o método prepareStatement como:
fonte
Como outros já declararam, seu código está basicamente correto, embora o externo
try
seja desnecessário. Aqui estão mais alguns pensamentos.DataSource
Outras respostas aqui são corretas e boas, como a resposta aceita por bpgergo. Mas nenhum dos programas mostra o uso de
DataSource
, comumente recomendado sobre o usoDriverManager
no Java moderno.Portanto, por uma questão de integridade, aqui está um exemplo completo que busca a data atual do servidor de banco de dados. O banco de dados usado aqui é o Postgres . Qualquer outro banco de dados funcionaria da mesma forma. Você substituiria o uso de
org.postgresql.ds.PGSimpleDataSource
por uma implementação deDataSource
apropriada ao seu banco de dados. Provavelmente, uma implementação é fornecida pelo seu driver ou pool de conexão específico, se você seguir esse caminho.Uma
DataSource
implementação não precisa ser fechada, porque nunca é "aberta". ADataSource
não é um recurso, não está conectado ao banco de dados e, portanto, não mantém conexões de rede nem recursos no servidor de banco de dados. ADataSource
são simplesmente as informações necessárias ao fazer uma conexão com o banco de dados, com o nome ou endereço da rede do servidor de banco de dados, o nome do usuário, a senha do usuário e as várias opções que você deseja especificar quando uma conexão for finalmente estabelecida. Portanto, seuDataSource
objeto de implementação não entra nos parênteses da tentativa com recursos.Tentativa aninhada com recursos
Seu código utiliza corretamente as instruções try-with-resources aninhadas.
Observe no código de exemplo abaixo que também usamos a sintaxe try-with-resources duas vezes , uma aninhada dentro da outra. O externo
try
define dois recursos:Connection
ePreparedStatement
. O internotry
define oResultSet
recurso. Essa é uma estrutura de código comum.Se uma exceção for lançada a partir da interna e não for capturada lá, o
ResultSet
recurso será fechado automaticamente (se existir, não será nulo). Depois disso, oPreparedStatement
será fechado, e por último oConnection
é fechado. Os recursos são fechados automaticamente na ordem inversa na qual foram declarados nas instruções try-with-resource.O código de exemplo aqui é excessivamente simplista. Conforme escrito, ele pode ser executado com uma única instrução try-with-resources. Mas, em um trabalho real, você provavelmente fará mais trabalho entre o par de
try
chamadas aninhadas . Por exemplo, você pode extrair valores de sua interface com o usuário ou de um POJO e passá-los para preencher?
espaços reservados em seu SQL por meio de chamadas paraPreparedStatement::set…
métodos.Notas de sintaxe
Ponto e vírgula à direita
Observe que o ponto-e-vírgula após a última instrução de recurso entre parênteses da tentativa com recursos é opcional. Eu o incluo no meu próprio trabalho por duas razões: Consistência e aparência completa, e facilita a cópia e a colagem de uma mistura de linhas sem ter que se preocupar com ponto e vírgula no final da linha. Seu IDE pode sinalizar o último ponto-e-vírgula como supérfluo, mas não há mal em deixá-lo.
Java 9 - Use vars existentes em try-with-resources
O que há de novo no Java 9 é um aprimoramento da sintaxe de tentativa com recursos. Agora podemos declarar e preencher os recursos fora dos parênteses do
try
declaração. Ainda não achei isso útil para os recursos JDBC, mas lembre-se de seu próprio trabalho.ResultSet
deve fechar-se, mas pode nãoEm um mundo ideal,
ResultSet
ele se fecha como a documentação promete:Infelizmente, no passado, alguns drivers JDBC falharam em cumprir essa promessa. Como resultado, muitos programadores JDBC aprendeu a fechar explicitamente todos os seus recursos JDBC incluindo
Connection
,PreparedStatement
eResultSet
também. A sintaxe moderna da tentativa com recursos tornou isso mais fácil e com código mais compacto. Observe que a equipe Java se deu ao trabalho de marcarResultSet
comoAutoCloseable
e sugiro que façamos uso disso. Usar uma tentativa com recursos em torno de todos os seus recursos JDBC torna seu código mais auto-documentado quanto às suas intenções.Exemplo de código
fonte