PreparedStatement
Sichere parametrisierte Abfragen
PreparedStatement ist eine kostenlose Java Academy-Lektion auf CoddyKit. Dies ist Lektion 2 von 4. Du kannst die komplette Lektion unten kostenlos lesen – dann übst du sie direkt im Browser mit einem integrierten Code-Editor und einem KI-Tutor rund um die Uhr. Sie ist Teil des Java Academy-Lernpfads, und dein Fortschritt wird über Web und CoddyKit-App synchronisiert. Der Java Academy-Kurs umfasst insgesamt 4 Lektionen.
Warum PreparedStatement?
SQL durch Verkettung von Benutzereingaben zu erstellen, ist gefährlich und langsam.
PreparedStatement löst beide Probleme:
- Sicherheit – Werte werden getrennt vom SQL gesendet, wodurch SQL-Injection verhindert wird.
- Leistung – Die Datenbank kann die Abfrage einmal analysieren und planen und anschließend wiederverwenden.
Das Problem der SQL-Injection
Betrachten Sie die Erstellung einer Abfrage durch Zeichenkettenverkettung. Wenn ein Benutzer '; DROP TABLE users; -- als Namen eingibt, führt das daraus zusammengesetzte SQL diesen Inhalt als Befehl aus.
Das ist SQL-Injection, eine der ältesten und schwerwiegendsten Sicherheitslücken im Web. PreparedStatement verhindert sie konstruktionsbedingt.
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);
}
}Platzhalter
In einem PreparedStatement schreiben Sie für jeden Wert, den Sie später angeben, ein ?.
Anschließend binden Sie die Werte mit typisierten Settern:
ps.setString(1, name)ps.setInt(2, age)
Die Parameterindizes beginnen bei 1 und entsprechen der Reihenfolge der ?-Platzhalter.
Eine sichere Abfrage
Erstellen Sie die Anweisung mit prepareStatement, binden Sie die Parameter und rufen Sie anschließend executeQuery auf.
Der Wert gelangt niemals in den SQL-Text. Daher ist unabhängig von der Eingabe des Benutzers keine Injection möglich.
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());
}
}
}Mehrere Parameter binden
Jeder ?-Platzhalter erhält seinen eigenen Setter-Aufruf. Die Reihenfolge ist wichtig: Parameter 1 gehört zum ersten ?, Parameter 2 zum zweiten und so weiter.
Verwenden Sie den Setter, der zum Spaltentyp passt – setInt, setString, setDouble, setBoolean und so weiter.
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());
}
}
}NULL verarbeiten
Um ein SQL-NULL zu binden, verwenden Sie setNull(index, type) mit einer Konstante aus java.sql.Types.
Übergeben Sie kein Java-null an setString und hoffen Sie, dass es funktioniert – seien Sie ausdrücklich:
ps.setNull(2, java.sql.Types.INTEGER)
Generierte Schlüssel abrufen
Nach einem INSERT benötigen Sie häufig die automatisch generierte ID.
Übergeben Sie beim Vorbereiten Statement.RETURN_GENERATED_KEYS und lesen Sie anschließend ps.getGeneratedKeys() aus.
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());
}
}
}Batch-Einfügungen
Tausende von Zeilen einzeln einzufügen, ist langsam. Verwenden Sie stattdessen Batches:
- Binden Sie die Parameter und rufen Sie anschließend
ps.addBatch()auf. - Wiederholen Sie dies für jede Zeile.
- Rufen Sie einmal
ps.executeBatch()auf.
Dadurch werden die Zeilen gemeinsam gesendet und die Anzahl der Roundtrips deutlich reduziert.
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());
}
}
}Ein Statement wiederverwenden
Ein einzelnes PreparedStatement kann mit unterschiedlichen Parametern viele Male ausgeführt werden.
Binden Sie die Platzhalter erneut und rufen Sie wieder execute auf. Die Datenbank verwendet den zwischengespeicherten Ausführungsplan erneut. Deshalb sind vorbereitete Anweisungen bei wiederholten Abfragen schneller.
Nie verketten
Die wichtigste Regel: Jeder veränderliche Wert muss ein ?-Parameter sein.
Nur feste, vom Code kontrollierte Bezeichner (z. B. ein auf einer Whitelist stehender Spaltenname) dürfen verkettet werden – und auch dann müssen Sie sie anhand einer Allowlist validieren. Vom Benutzer bereitgestellte Daten werden immer gebunden, niemals verkettet.
Schließen und Wiederverwenden
Ein PreparedStatement ist an seine Connection gebunden und sollte nach der Verwendung geschlossen werden.
Deklarieren Sie das Statement innerhalb von try-with-resources, damit es vor der Verbindung geschlossen wird. Wenn Sie ein Statement in einer Schleife viele Male wiederverwenden, bereiten Sie es außerhalb der Schleife einmal vor und binden Sie innerhalb der Schleife nur die Parameter erneut – bei jeder Iteration eine neue Vorbereitung durchzuführen, verwirft den zwischengespeicherten Plan.
Kurztest
Warum verhindert ein PreparedStatement SQL-Injection?
Zusammenfassung
Sie haben gelernt, sichere und schnelle Abfragen zu schreiben:
- Verwenden Sie
?-Platzhalter und typisierte Setter (Index ab 1). - Binden Sie Werte mit
setString,setInt,setNullusw. - Rufen Sie automatisch generierte Schlüssel mit
RETURN_GENERATED_KEYSundgetGeneratedKeysab. - Bündeln Sie viele Schreibvorgänge mit
addBatch+executeBatch. - Verketten Sie Benutzereingaben niemals mit SQL.
Häufig gestellte Fragen
Ist die Lektion „PreparedStatement“ kostenlos?
Ja — der vollständige Text von „PreparedStatement“ ist hier im Web kostenlos zu lesen. Um sie interaktiv zu üben (integrierter Code-Editor und 24/7 KI-Tutor) und den Rest des Java Academy-Kurses freizuschalten, upgrade auf CoddyKit PRO. Der Java Academy-Kurs umfasst insgesamt 4 Lektionen.
Was lerne ich in „PreparedStatement“?
Sichere parametrisierte Abfragen Du übst Java Academy mit praktischem Code, den du direkt im Browser ausführst, und ein 24/7 KI-Tutor beantwortet deine Fragen während du die Lektion bearbeitest.
Brauche ich Erfahrung, um Java Academy zu starten?
Keine Vorkenntnisse erforderlich. Java Academy auf CoddyKit ist für Anfänger bis fortgeschrittene Lernende strukturiert, sodass du hier starten oder von Anfang an beginnen und in deinem eigenen Tempo voranschreiten kannst. Dies ist Lektion 2 von 4.
Wie lange dauert die Lektion „PreparedStatement“?
Die meisten CoddyKit-Lektionen dauern etwa 5–10 Minuten. Jede ist kompakt und interaktiv, sodass du stetig Fortschritte machst und genau dort weitermachst, wo du aufgehört hast – im Web und in der App.
Kann ich in dieser Java Academy-Lektion Code schreiben und ausführen?
Ja. Jede Java Academy-Lektion enthält einen integrierten Code-Editor, sodass du echten Code direkt in deinem Browser schreibst und ausführst und sofort KI-Feedback erhältst — ohne lokale Einrichtung erforderlich.
Alle Lektionen in diesem Kurs
- JDBC-Grundlagen
- PreparedStatement
- Transaktionen
- Connection Pooling mit HikariCP