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,setNulletc. - Recupere chaves geradas automaticamente com
RETURN_GENERATED_KEYSegetGeneratedKeys. - 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
- Fundamentos de JDBC
- PreparedStatement
- Transações
- Pool de conexões com HikariCP