0Pricing
Java Academy · Aula

PreparedStatement

Consultas parametrizadas seguras.

PreparedStatement é uma aula grátis de Java Academy no CoddyKit. Esta é a aula 2 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de Java Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de Java Academy inclui 4 aulas no total.

Por que usar PreparedStatement?

Montar SQL concatenando a entrada do usuário é perigoso e lento.

PreparedStatement resolve os dois problemas:

  • Segurança — os valores são enviados separadamente do SQL, impedindo a injeção de SQL.
  • Desempenho — o banco de dados pode analisar e planejar a consulta uma vez e depois reutilizá-la.

O problema da injeção de SQL

Considere montar uma consulta concatenando strings. Se um usuário digitar '; DROP TABLE users; -- como nome, o SQL concatenado executará isso como um comando.

Isso é uma injeção de SQL, uma das vulnerabilidades da web mais antigas e prejudiciais. O PreparedStatement a torna impossível por definição.

public class Main {
    public static void main(String[] args) {
        String userInput = "'; DROP TABLE users; --";
        // Dangerous: never do this
        String unsafe = "SELECT * FROM users WHERE name = '" + userInput + "'";
        System.out.println("Injected SQL becomes:");
        System.out.println(unsafe);
    }
}

Marcadores de posição

Em um PreparedStatement, escreva um ? para cada valor que você fornecerá depois.

Em seguida, associe os valores usando métodos tipados de atribuição:

  • ps.setString(1, name)
  • ps.setInt(2, age)

Os índices dos parâmetros começam em 1, correspondendo à ordem dos marcadores ?.

Uma consulta segura

Crie a instrução com prepareStatement, associe os parâmetros e depois chame executeQuery.

O valor nunca entra em contato com o texto SQL, portanto a injeção não pode ocorrer, independentemente do que o usuário digite.

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

public class Main {
    public static void main(String[] args) {
        String url = "jdbc:postgresql://localhost:5432/shop";
        String sql = "SELECT id FROM users WHERE name = ?";
        try (Connection conn = DriverManager.getConnection(url, "app", "secret");
             PreparedStatement ps = conn.prepareStatement(sql)) {
            ps.setString(1, "Ada");
            try (ResultSet rs = ps.executeQuery()) {
                while (rs.next()) System.out.println(rs.getInt("id"));
            }
        } catch (SQLException e) {
            System.out.println(e.getMessage());
        }
    }
}

Associando vários parâmetros

Cada ? recebe sua própria chamada de atribuição. A ordem é importante: o parâmetro 1 corresponde ao primeiro ?, o parâmetro 2 ao segundo, e assim por diante.

Use o método de atribuição correspondente ao tipo da coluna — setInt, setString, setDouble, setBoolean e assim por diante.

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class Main {
    public static void main(String[] args) {
        String url = "jdbc:postgresql://localhost:5432/shop";
        String sql = "INSERT INTO users(name, age, active) VALUES (?, ?, ?)";
        try (Connection conn = DriverManager.getConnection(url, "app", "secret");
             PreparedStatement ps = conn.prepareStatement(sql)) {
            ps.setString(1, "Grace");
            ps.setInt(2, 42);
            ps.setBoolean(3, true);
            int rows = ps.executeUpdate();
            System.out.println("Inserted " + rows + " row");
        } catch (SQLException e) {
            System.out.println(e.getMessage());
        }
    }
}

Tratando NULL

Para associar um NULL do SQL, use setNull(index, type) com uma constante de java.sql.Types.

Não passe um null de Java para setString esperando que funcione — seja explícito:

  • ps.setNull(2, java.sql.Types.INTEGER)

Obtendo chaves geradas

Depois de um INSERT, geralmente você precisa do identificador gerado automaticamente.

Passe Statement.RETURN_GENERATED_KEYS ao preparar a instrução e depois leia ps.getGeneratedKeys().

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class Main {
    public static void main(String[] args) {
        String url = "jdbc:postgresql://localhost:5432/shop";
        String sql = "INSERT INTO users(name) VALUES (?)";
        try (Connection conn = DriverManager.getConnection(url, "app", "secret");
             PreparedStatement ps = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
            ps.setString(1, "Linus");
            ps.executeUpdate();
            try (ResultSet keys = ps.getGeneratedKeys()) {
                if (keys.next()) System.out.println("New id: " + keys.getLong(1));
            }
        } catch (SQLException e) {
            System.out.println(e.getMessage());
        }
    }
}

Inserções em lote

Inserir milhares de linhas, uma instrução por vez, é lento. Use o processamento em lote:

  • Associe os parâmetros e depois chame ps.addBatch().
  • Repita o processo para cada linha.
  • Chame ps.executeBatch() uma única vez.

Isso envia as linhas em conjunto, reduzindo drasticamente as comunicações com o banco de dados.

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class Main {
    public static void main(String[] args) {
        String url = "jdbc:postgresql://localhost:5432/shop";
        String sql = "INSERT INTO tags(label) VALUES (?)";
        String[] labels = {"java", "jdbc", "sql"};
        try (Connection conn = DriverManager.getConnection(url, "app", "secret");
             PreparedStatement ps = conn.prepareStatement(sql)) {
            for (String label : labels) {
                ps.setString(1, label);
                ps.addBatch();
            }
            int[] counts = ps.executeBatch();
            System.out.println("Batched " + counts.length + " inserts");
        } catch (SQLException e) {
            System.out.println(e.getMessage());
        }
    }
}

Reutilizando uma instrução

Um único PreparedStatement pode ser executado muitas vezes com parâmetros diferentes.

Associe novamente os marcadores e chame o método de execução outra vez. O banco de dados reutiliza o plano de execução armazenado em cache, e é por isso que as instruções preparadas são mais rápidas para consultas repetidas.

Nunca concatene

A regra de ouro: qualquer valor variável deve ser um parâmetro ?.

Somente identificadores fixos controlados pelo código (como um nome de coluna incluído em uma lista permitida) podem ser concatenados — e, mesmo assim, valide-os usando uma lista de permissões. Dados fornecidos pelo usuário sempre devem ser associados, nunca concatenados.

Escopo de fechamento e reutilização

Um PreparedStatement está vinculado à sua Connection e deve ser fechado quando terminar de usá-lo.

Dentro do bloco de tentativa com recursos, declare a instrução para que ela seja fechada antes da conexão. Se você reutilizar uma instrução muitas vezes em um laço, prepare-a uma única vez fora do laço e apenas associe novamente os parâmetros dentro dele — preparar novamente a cada iteração descarta o plano armazenado em cache.

Verificação rápida

Por que um PreparedStatement impede a injeção de SQL?

Recapitulação

Você aprendeu a escrever consultas seguras e rápidas:

  • Utilize marcadores ? e métodos de definição tipados (índice começando em 1).
  • Vincule valores com setString, setInt, setNull etc.
  • Recupere chaves geradas automaticamente com RETURN_GENERATED_KEYS e getGeneratedKeys.
  • Execute muitas operações de escrita em lote com addBatch + executeBatch.
  • Nunca concatene a entrada do usuário ao SQL.

Perguntas Frequentes

A aula “PreparedStatement” é grátis?

Sim — o texto completo de “PreparedStatement” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de Java Academy, atualize para CoddyKit PRO. O curso de Java Academy inclui 4 aulas no total.

O que vou aprender em “PreparedStatement”?

Consultas parametrizadas seguras. Você pratica Java Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.

Preciso ter experiência prévia para começar Java Academy?

Nenhuma experiência prévia é necessária. Java Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 2 de 4.

Quanto tempo leva a aula “PreparedStatement”?

A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.

Posso escrever e executar código nesta aula de Java Academy?

Sim. Cada aula de Java Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.

Todas as aulas deste curso

  1. Fundamentos de JDBC
  2. PreparedStatement
  3. Transações
  4. Pool de conexões com HikariCP
← Voltar para Java Academy