Conexão JDBC em Java
Abra e gerencie conexões com bancos de dados em Java com a interface Connection — abrir, fechar e configurar.
Uma Connection é uma sessão ativa com o banco de dados. É o objeto que você recebe do DriverManager (ou de um DataSource), e é a fábrica para tudo o mais — statements, transações, savepoints, metadados. Uma conexão também é um recurso escasso e custoso: cada uma aberta ocupa um socket e uma sessão no servidor, portanto a regra fundamental é abrir tarde, fechar rapidamente.
Este capítulo aborda como construir uma URL de conexão, as três formas de abrir uma Connection, por que try-with-resources é indispensável, as configurações de sessão que você pode ajustar e os erros que você encontrará quando algo estiver mal configurado. Pressupõe-se que você já carregou um driver — consulte JDBC Drivers e a Introdução ao JDBC para uma visão mais ampla.
A URL de conexão
Tudo o que o DriverManager precisa para encontrar e acessar um banco de dados é codificado na URL:
jdbc:<subprotocol>://<host>:<port>/<database>?<key=value&...>Por exemplo, jdbc:postgresql://db.internal:5432/shop?ssl=true. O prefixo jdbc: é obrigatório; o subprotocolo seleciona o driver; o restante é específico do fornecedor, mas convencionalmente inclui host, porta, banco de dados e uma query string com opções de configuração. Algumas URLs reais para reconhecer:
| Banco de Dados | Exemplo de URL |
|---|---|
| PostgreSQL | jdbc:postgresql://localhost:5432/shop |
| MySQL | jdbc:mysql://localhost:3306/shop?useSSL=true |
| H2 (em memória) | jdbc:h2:mem:testdb |
| SQLite (arquivo) | jdbc:sqlite:/data/shop.db |
O subprotocolo (postgresql, mysql, h2, sqlite) é como o DriverManager decide qual driver registrado deve tratar a URL, portanto acertá-lo é o que diz ao JDBC qual banco de dados você quer dizer.
Três formas de abrir uma conexão
// 1. URL with credentials as arguments
Connection a = DriverManager.getConnection(url, "app", "secret");
// 2. URL with a Properties bag (user, password, plus driver-specific keys)
Properties props = new Properties();
props.setProperty("user", "app");
props.setProperty("password", "secret");
props.setProperty("connectTimeout", "10");
Connection b = DriverManager.getConnection(url, props);
// 3. From a pooled DataSource (preferred for applications)
Connection c = dataSource.getConnection();DriverManager vs. DataSource
DriverManager.getConnection(...) abre uma conexão física completamente nova a cada vez e a encerra quando você a fecha. Esse handshake — consulta DNS, TCP, TLS, autenticação — custa dezenas de milissegundos, o que é aceitável para um script, mas ruinoso para um servidor que trata muitas requisições.
Um DataSource apoiado por um pool de conexões (HikariCP, Apache DBCP ou o que o servidor de aplicação fornece) mantém um conjunto de conexões físicas abertas e as distribui. Chamar getConnection() empresta uma; chamar close() a devolve ao pool em vez de encerrá-la de verdade. Para qualquer aplicação de longa duração, prefira um DataSource com pool; recorra ao DriverManager apenas em pequenas ferramentas, testes e exemplos.
Sempre feche — use try-with-resources
Connection, Statement e ResultSet implementam AutoCloseable. Declará-los no cabeçalho de um try-with-resources garante que sejam fechados em ordem inversa mesmo que uma exceção seja lançada — o hábito mais importante no JDBC:
try (Connection conn = DriverManager.getConnection(url, "app", "secret")) {
// use conn...
} // conn.close() runs here automatically, even on exceptionVazar conexões (esquecer de fechar) esgota o pool e eventualmente trava toda a aplicação — uma clássica falha em produção. Note que abrir uma Connection lança uma SQLException verificada, portanto a chamada sempre fica dentro de um try (ou em um método que declara throws SQLException).
Uma vez que você tenha uma conexão, pode usá-la para criar Statements e PreparedStatements — e eles devem ficar dentro do mesmo cabeçalho try-with-resources para que sejam fechados antes da conexão:
try (Connection conn = dataSource.getConnection();
PreparedStatement ps = conn.prepareStatement("SELECT name FROM users WHERE id = ?")) {
ps.setInt(1, 42);
// ...read the ResultSet...
} // ps closes first, then conn — reverse declaration orderConfigurando uma conexão
Uma vez aberta, uma conexão carrega configurações de nível de sessão:
| Método | O que faz |
|---|---|
setAutoCommit(false) | Inicia uma transação manual; você então chama commit() ou rollback() manualmente. Consulte JDBC Transactions. |
setTransactionIsolation(...) | Define um nível de isolamento (por exemplo, TRANSACTION_READ_COMMITTED). |
setReadOnly(true) | Uma dica de que a conexão não realiza escritas; alguns drivers a otimizam para isso. |
setSchema(...) / setCatalog(...) | Seleciona o namespace contra o qual as consultas são executadas. |
isValid(timeout) | Retorna true se a conexão ainda está ativa; pools usam isso para descartar conexões mortas. |
Essas configurações são por sessão, portanto são redefinidas quando uma conexão real é fechada — mas com um pool, uma conexão emprestada pode ainda carregar configurações deixadas por um usuário anterior. É por isso que os pools redefinem autoCommit e isolamento ao devolver, e por que você deve definir o que precisa em vez de assumir os padrões.
Erros comuns de conexão
Quando uma conexão falha, você quase sempre recebe uma SQLException; a mensagem indica qual camada falhou:
No suitable driver found for ...— a classe do driver nunca foi carregada, ou o subprotocolo da URL está com erro de digitação, de modo que nenhum driver registrado a reivindica. Corrija a dependência ou a URL (consulte JDBC Drivers).Connection refused— nada está ouvindo nesse host/porta: o banco de dados está inativo ou o host/porta na URL está errado.- Authentication /
password authentication failed—useroupasswordincorretos, ou o usuário não tem direitos sobre esse banco de dados. - Connection timeout — o host está inacessível (firewall, rede incorreta). Defina
connectTimeoutpara que a chamada falhe rapidamente em vez de travar.
Um exemplo prático: a anatomia de uma URL de conexão
Este programa desmonta uma URL JDBC realista em seus componentes e constrói o objeto Properties que você passaria junto com ela — as duas entradas que getConnection precisa — sem exigir um banco de dados ativo.
O que aprender com a execução:
- A URL não é opaca — é dado estruturado.
getConnectionanalisa exatamente estas partes: o subprotocolo escolhe o driver, e o host/porta/banco de dados dizem a esse driver onde conectar. Ler uma URL em voz alta é a forma mais rápida de depurar erros de "banco de dados errado". - A query string (
?ssl=true&applicationName=reports) carrega opções específicas do driver. As mesmas configurações podem estar na URL ou no objetoProperties— ambas chegam ao driver, e você os mistura a seu gosto. - As credenciais pertencem ao
Properties(ou à configuração doDataSource), não codificadas diretamente na string de URL que você registra. O exemplo mascara a senha na saída exatamente por esse motivo — nunca registre credenciais em log. connectTimeouté uma propriedade real do driver PostgreSQL. O ajuste fica nesses pares chave/valor, razão pela qual você raramente precisa criar subclasses: é a configuração, não o código, que molda uma conexão.- Isso foi executado sem um banco de dados porque construir as entradas para
getConnectioné puro trabalho com strings. A parte custosa — o socket e a sessão no servidor — só acontece na chamada degetConnectionem si, razão pela qual você a adia e a fecha rapidamente.