Hospedagem Segura — página inicial

Docker e aplicações

Conexão PostgreSQL em Java: passo a passo com JDBC, SSL e pool

Do pom.xml ao primeiro SELECT: como conectar uma aplicação Java ao PostgreSQL com o driver pgJDBC, guardar a senha fora do código, exigir SSL com verificação do certificado, usar o pool HikariCP e resolver Connection refused, password authentication failed e no pg_hba.conf entry.

  • Por equipe técnica da Hospedagem Segura
  • Atualizado em 10/10/2026
  • 11 min de leitura

Texto do artigo

Neste artigo
  1. Dependência do driver pgJDBC (Maven e Gradle)
  2. URL de conexão e credenciais fora do código
  3. Conexão PostgreSQL em Java com try-with-resources e PreparedStatement
  4. Conexão segura: SSL com sslmode verify-full
  5. Pool de conexões com HikariCP
  6. Erros comuns de conexão e como resolver
  7. Onde rodar a aplicação Java e o PostgreSQL
  8. Perguntas frequentes

Para fazer a conexão PostgreSQL em Java, você precisa de três coisas: o driver oficial pgJDBC no projeto, uma URL no formato jdbc:postgresql://servidor:5432/banco e um usuário que o PostgreSQL aceite vindo do IP da aplicação. Com isso, DriverManager.getConnection devolve a conexão; em produção, quem entrega as conexões é um pool como o HikariCP.

Este passo a passo foi testado com Java 21 (Eclipse Temurin), PostgreSQL 17.11 e pgJDBC 42.7.14, a versão mais recente do driver em outubro de 2026. No fim estão os três erros que mais aparecem, reproduzidos de verdade, e como resolver cada um: Connection refused, password authentication failed e no pg_hba.conf entry.

Dependência do driver pgJDBC (Maven e Gradle)

O driver do PostgreSQL para Java é o pgJDBC, escrito em Java puro e publicado no Maven Central. No Maven, a dependência é esta:

<dependency>
  <groupId>org.postgresql</groupId>
  <artifactId>postgresql</artifactId>
  <version>42.7.14</version>
</dependency>
No Gradle: implementation("org.postgresql:postgresql:42.7.14").

A 42.7.14 roda em Java 8 ou mais novo e conecta ao PostgreSQL 8.4 em diante (os testes do projeto cobrem a partir da 9.1). Ela saiu em 7/10/2026 com duas correções de segurança, então vale atualizar quem está numa 42.7.x anterior. Sem Maven nem Gradle, baixe o JAR do Maven Central e coloque-o no classpath; o artigo sobre drivers JDBC mostra como baixar e conferir o hash.

URL de conexão e credenciais fora do código

A URL segue o formato jdbc:postgresql://servidor:porta/banco. Sem servidor, o driver usa localhost; sem porta, 5432; sem banco, um banco com o mesmo nome do usuário. Os parâmetros vêm depois do ponto de interrogação, separados por &. Os mais úteis:

  • sslmode: disable, allow, prefer (o padrão), require, verify-ca ou verify-full.
  • connectTimeout: quantos segundos esperar para abrir a conexão (padrão 10).
  • socketTimeout: limite de espera por uma resposta, em segundos (padrão 0, sem limite).
  • ApplicationName: nome que aparece na view pg_stat_activity do servidor, ótimo para saber quem está conectado.
  • currentSchema: o schema usado quando a consulta não informa um.

Usuário e senha não entram no código nem no repositório. No servidor da aplicação, guarde-os num arquivo legível só pelo usuário que roda o Java e carregue as variáveis antes de iniciar:

DB_URL="jdbc:postgresql://db.exemplo.com.br:5432/loja?sslmode=verify-full&ApplicationName=loja-api"
DB_USER="app"
DB_PASSWORD="troque-por-uma-senha-forte"
banco.env. As aspas evitam que o shell interprete o & da URL. Proteja com chmod 600 banco.env e carregue com set -a; . ./banco.env; set +a. Num serviço do systemd, o mesmo arquivo entra na diretiva EnvironmentFile.

Conexão PostgreSQL em Java com try-with-resources e PreparedStatement

O programa abaixo conecta, mostra com qual usuário entrou e se a conexão está criptografada, e lista os produtos até um preço informado na linha de comando:

import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class ConexaoPostgres {

    public static void main(String[] args) {
        String url = System.getenv("DB_URL");
        String usuario = System.getenv("DB_USER");
        String senha = System.getenv("DB_PASSWORD");
        BigDecimal limite = new BigDecimal(args.length > 0 ? args[0] : "100");

        try (Connection conn = DriverManager.getConnection(url, usuario, senha)) {
            mostraConexao(conn);
            listaProdutos(conn, limite);
        } catch (SQLException e) {
            System.err.println("Erro: " + e.getMessage());
            System.err.println("SQLState: " + e.getSQLState());
            System.exit(1);
        }
    }

    static void mostraConexao(Connection conn) throws SQLException {
        String sql = "SELECT current_user, ssl, coalesce(version, '-') FROM pg_stat_ssl WHERE pid = pg_backend_pid()";
        try (PreparedStatement ps = conn.prepareStatement(sql);
             ResultSet rs = ps.executeQuery()) {
            rs.next();
            System.out.println("Conectado como " + rs.getString(1) + " ao PostgreSQL "
                    + conn.getMetaData().getDatabaseProductVersion());
            System.out.println("SSL: " + (rs.getBoolean(2) ? "sim, " + rs.getString(3) : "não"));
        }
    }

    static void listaProdutos(Connection conn, BigDecimal limite) throws SQLException {
        String sql = "SELECT id, nome, preco FROM produtos WHERE preco <= ? ORDER BY preco";
        try (PreparedStatement ps = conn.prepareStatement(sql)) {
            ps.setBigDecimal(1, limite);
            try (ResultSet rs = ps.executeQuery()) {
                System.out.println("Produtos até R$ " + limite + ":");
                while (rs.next()) {
                    System.out.printf("  %d  %-16s %8s%n",
                            rs.getInt("id"), rs.getString("nome"), rs.getBigDecimal("preco"));
                }
            }
        }
    }
}
ConexaoPostgres.java, compilado e executado com Java 21.

Três detalhes fazem diferença em produção. O try-with-resources fecha o ResultSet, o PreparedStatement e a Connection mesmo quando ocorre uma exceção; sem isso, cada erro deixa uma conexão aberta, até o servidor recusar novas com "too many clients". O PreparedStatement envia o SQL com marcadores ? e os valores separados, o que impede SQL injection e cuida dos tipos (BigDecimal para dinheiro, nunca double). E o SQLState impresso no catch identifica o tipo de falha: 08001 para problema de rede, 28P01 para senha, 28000 para regra do pg_hba.conf.

Terminal carregando as variáveis do banco.env, compilando ConexaoPostgres.java e listando os produtos do PostgreSQL 17.11
Conexão feita: usuário app, PostgreSQL 17.11 e a consulta com PreparedStatement. O SSL ainda está desligado.

Para gravar, a lógica é a mesma, com uma transação: desligue o autocommit, execute e confirme com commit, ou desfaça com rollback se algo falhar. O RETURNING id devolve a chave gerada sem uma segunda consulta:

static int insere(Connection conn, String nome, BigDecimal preco) throws SQLException {
    String sql = "INSERT INTO produtos (nome, preco) VALUES (?, ?) RETURNING id";
    conn.setAutoCommit(false);
    try (PreparedStatement ps = conn.prepareStatement(sql)) {
        ps.setString(1, nome);
        ps.setBigDecimal(2, preco);
        try (ResultSet rs = ps.executeQuery()) {
            rs.next();
            int id = rs.getInt(1);
            conn.commit();
            return id;
        }
    } catch (SQLException e) {
        conn.rollback();
        throw e;
    }
}

Conexão segura: SSL com sslmode verify-full

No teste anterior, a linha "SSL: não" mostra que os dados, incluindo a autenticação, trafegaram sem criptografia. O padrão do driver, sslmode=prefer, só criptografa se o servidor oferecer e não confere o certificado; require também não confere. Para proteger a conexão de quem estiver no meio do caminho, use verify-full: o driver valida o certificado do servidor contra uma autoridade em que você confia e confere se o nome do servidor bate com o certificado.

No servidor do banco, ligue o SSL e, no pg_hba.conf, troque host por hostssl para recusar qualquer conexão sem criptografia:

# postgresql.conf
ssl = on
ssl_cert_file = 'server.crt'
ssl_key_file = 'server.key'

# pg_hba.conf: só o usuário app, no banco loja, vindo do IP da aplicação, com SSL
hostssl loja app 198.51.100.10/32 scram-sha-256
Depois de editar, recarregue com SELECT pg_reload_conf(); as duas mudanças valem sem reiniciar o PostgreSQL.

No servidor da aplicação, o certificado da autoridade que assinou o do banco fica em ~/.postgresql/root.crt, o caminho padrão do driver no Linux (no Windows, %APPDATA%\postgresql\root.crt), ou em outro arquivo indicado pelo parâmetro sslrootcert. Se o servidor estiver com o SSL desligado, qualquer sslmode a partir de require falha com "The server does not support SSL."

Terminal mostrando a conexão com sslmode=verify-full aceita pelo nome do servidor, recusada pelo IP e a conexão sem SSL recusada pelo pg_hba.conf
Com verify-full pelo nome, a conexão sai em TLSv1.3. Pelo IP, o certificado não confere; e sem SSL, o hostssl recusa.

A segunda tentativa da imagem mostra o verify-full trabalhando: o certificado foi emitido para db.exemplo.com.br, então a conexão pelo IP é recusada. Conecte sempre pelo nome que está no certificado.

Pool de conexões com HikariCP

Abrir conexão no PostgreSQL custa caro: o servidor cria um processo para cada uma, e há ainda a negociação de SSL e a autenticação. Numa API, abrir uma conexão por requisição pesa no tempo de resposta e esbarra no max_connections, que vem com 100. O HikariCP mantém algumas conexões abertas e as reaproveita. A versão atual é a 7.1.0, para Java 11 ou mais novo; ele registra os eventos pelo SLF4J, por isso o slf4j-simple (ou o logger que o projeto já usa):

<dependency>
  <groupId>com.zaxxer</groupId>
  <artifactId>HikariCP</artifactId>
  <version>7.1.0</version>
</dependency>
<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-simple</artifactId>
  <version>2.0.17</version>
</dependency>
Dependências do HikariCP e do SLF4J no pom.xml, além do pgJDBC.
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

public class PoolHikari {
    public static void main(String[] args) throws Exception {
        HikariConfig config = new HikariConfig();
        config.setJdbcUrl(System.getenv("DB_URL"));
        config.setUsername(System.getenv("DB_USER"));
        config.setPassword(System.getenv("DB_PASSWORD"));
        config.setPoolName("loja");
        config.setMaximumPoolSize(5);

        try (HikariDataSource ds = new HikariDataSource(config)) {
            for (int i = 1; i <= 3; i++) {
                try (Connection conn = ds.getConnection();
                     PreparedStatement ps = conn.prepareStatement("SELECT pg_backend_pid()");
                     ResultSet rs = ps.executeQuery()) {
                    rs.next();
                    System.out.println("Consulta " + i + ": conexão do pool, processo " + rs.getInt(1) + " no servidor");
                }
            }
        }
    }
}
PoolHikari.java. O close() da conexão, feito pelo try-with-resources, devolve a conexão ao pool em vez de fechá-la.
Terminal executando PoolHikari com o HikariCP 7.1.0: o pool inicia, as três consultas usam o mesmo processo do servidor e o pool encerra
As três consultas usaram o mesmo processo 121 no servidor: a conexão foi reaproveitada pelo pool.

O padrão do HikariCP é de até 10 conexões por pool. Some o pool de todas as instâncias da aplicação e deixe o total abaixo do max_connections, com folga para manutenção e backup. No Spring Boot, o HikariCP já é o pool padrão: basta informar spring.datasource.url, username e password.

Erros comuns de conexão e como resolver

Connection refused

O pedido chegou ao endereço, mas nada aceitou a conexão na porta 5432. As causas mais comuns: o PostgreSQL está parado, está configurado para escutar só em localhost (o padrão do listen_addresses) ou um firewall rejeita a porta. Teste a porta a partir do servidor da aplicação, sem o Java no meio:

Terminal com o erro Connection to db.exemplo.com.br:5432 refused no Java e o teste de porta pelo bash confirmando a porta fechada
O driver devolve SQLState 08001; o teste pelo bash confirma que a porta 5432 recusa conexões vindas da aplicação.

No servidor do banco, confira com SHOW listen_addresses; no psql. Para aceitar conexões de outra máquina, informe o IP da interface de rede no postgresql.conf e reinicie o PostgreSQL, porque esse parâmetro só muda na inicialização:

listen_addresses = 'localhost,198.51.100.20'

Libere a porta 5432 no firewall apenas para o IP da aplicação. Se a aplicação e o banco ficam na mesma VPS, nada disso é necessário: conecte por localhost e mantenha a porta fechada para a internet.

password authentication failed for user

O servidor encontrou uma regra no pg_hba.conf para a conexão, mas a senha não confere. A aplicação recebe a mesma mensagem quando o usuário não existe; só o log do servidor mostra a diferença e qual linha do pg_hba.conf foi usada:

2026-10-10 02:54:01.534 UTC [140] FATAL:  password authentication failed for user "app"
2026-10-10 02:54:01.534 UTC [140] DETAIL:  Connection matched file "/var/lib/postgresql/data/pg_hba.conf" line 128: "host    loja            app             198.51.100.10/32        scram-sha-256"
Linhas reais do log do PostgreSQL 17 no teste. Para usuário inexistente, o DETAIL diz Role "nome" does not exist.

Confira a senha no banco.env e, se precisar, redefina no psql com \password app, que não deixa a senha no histórico.

Terminal com os erros password authentication failed for user app e no pg_hba.conf entry for host, com os SQLStates 28P01 e 28000
Senha errada devolve 28P01; banco, usuário ou IP fora das regras do pg_hba.conf devolve 28000.

no pg_hba.conf entry for host

Nenhuma linha do pg_hba.conf combina com a conexão. A mensagem traz tudo o que você precisa comparar: o IP de origem, o usuário, o banco e se havia criptografia. No exemplo, a aplicação tentou o banco postgres, mas a regra só libera o banco loja. Os casos mais comuns são a aplicação mudar de servidor (IP novo), o nome do banco errado na URL e a regra hostssl com uma conexão sem SSL. Para ver o arquivo em uso e as regras que o servidor carregou:

SHOW hba_file;
SELECT line_number, type, database, user_name, address, auth_method
  FROM pg_hba_file_rules;

Ajuste a linha, ou acrescente uma para o novo IP, e recarregue com SELECT pg_reload_conf(); sem reiniciar o banco.

Onde rodar a aplicação Java e o PostgreSQL

Uma aplicação Java com PostgreSQL próprio precisa de um servidor em que você escolha as versões, edite o postgresql.conf e o pg_hba.conf e controle o firewall. A VPS Linux da Hospedagem Segura entrega acesso root, armazenamento NVMe e IPv4 dedicado, com Ubuntu, Debian, AlmaLinux ou Rocky Linux, para rodar o Java e o PostgreSQL na mesma máquina ou em VPS separadas. A administração e a estratégia de backup do banco ficam com você; quem prefere receber a VPS pronta, com backups periódicos e ajuda técnica mensal, pode escolher o VPS Assistido.

Perguntas frequentes

Qual é a URL de conexão do PostgreSQL em Java?

É jdbc:postgresql://servidor:5432/nome_do_banco, com parâmetros opcionais depois do ponto de interrogação, como sslmode=verify-full. Sem a porta, o driver usa 5432; sem o servidor, localhost.

Preciso de Class.forName("org.postgresql.Driver")?

Não. Com o JAR do pgJDBC no classpath, o driver se registra sozinho pelo mecanismo de service provider do Java, e DriverManager.getConnection já o encontra. O Class.forName só era obrigatório em versões muito antigas do Java.

Qual versão do pgJDBC usar com Java 21?

Use a 42.7.14, a mais recente em outubro de 2026, que funciona com Java 8 ou mais novo, incluindo o Java 21. Ela corrigiu duas falhas de segurança das versões 42.7.x anteriores.

Como forçar SSL na conexão do Java com o PostgreSQL?

No cliente, use sslmode=verify-full na URL e tenha o certificado da autoridade em ~/.postgresql/root.crt ou no parâmetro sslrootcert. No servidor, ligue ssl = on e use hostssl no pg_hba.conf, para que conexões sem criptografia sejam recusadas.

Como resolver "no pg_hba.conf entry for host"?

Acrescente ao pg_hba.conf uma linha que combine com o IP, o usuário e o banco citados na mensagem e recarregue a configuração com SELECT pg_reload_conf();. Confira também se a URL aponta para o banco certo e se a regra exige SSL (hostssl) enquanto o cliente conecta sem.

Java e PostgreSQL no seu servidor

VPS Linux com acesso root, armazenamento NVMe e IPv4 dedicado para rodar a sua aplicação Java e o PostgreSQL com a configuração que você precisa.