PreparedStatement
Säkra parameteriserade frågor.
PreparedStatement är en gratis lektion i Java Academy på CoddyKit. Detta är lektion 2 av 4. Ni kan läsa hela lektionen gratis nedan och sedan öva praktiskt i webbläsaren med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt. Den ingår i lärvägen för Java Academy, och Era framsteg synkroniseras mellan webben och CoddyKit-appen. Kursen i Java Academy innehåller totalt 4 lektioner.
Varför PreparedStatement?
Att bygga SQL genom att konkatenera användarinmatning är farligt och långsamt.
PreparedStatement löser båda problemen:
- Säkerhet — värden skickas separat från SQL-koden, vilket förhindrar SQL-injektion.
- Prestanda — databasen kan tolka och planera frågan en gång och sedan återanvända planen.
Problemet med SQL-injektion
Anta att Ni bygger en fråga genom strängkonkatenering. Om en användare skriver '; DROP TABLE users; -- som sitt namn, körs den sammanfogade SQL-koden som ett kommando.
Detta är SQL-injektion, en av de äldsta och mest skadliga sårbarheterna i webbsammanhang. PreparedStatement gör det omöjligt genom sin konstruktion.
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);
}
}Platshållare
I en PreparedStatement skriver Ni ? för varje värde som Ni vill ange senare.
Bind sedan värdena med typade setter-metoder:
ps.setString(1, name)ps.setInt(2, age)
Parameterindex är 1-baserade, i samma ordning som ?-tecknen.
En säker fråga
Skapa satsen med prepareStatement, bind parametrarna och anropa sedan executeQuery.
Värdet kommer aldrig i kontakt med SQL-texten, så injektion kan inte ske oavsett vad användaren skriver.
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());
}
}
}Binda flera parametrar
Varje ? får sitt eget setter-anrop. Ordningen spelar roll: parameter 1 är det första ?, parameter 2 är det andra och så vidare.
Använd setter-metoden som motsvarar kolumnens typ — setInt, setString, setDouble, setBoolean och så vidare.
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());
}
}
}Hantera NULL
För att binda ett SQL-NULL använder Ni setNull(index, type) med en konstant från java.sql.Types.
Skicka inte ett Java-null till setString och hoppas att det fungerar — var tydlig:
ps.setNull(2, java.sql.Types.INTEGER)
Hämta genererade nycklar
Efter en INSERT behöver Ni ofta det automatiskt genererade id:t.
Ange Statement.RETURN_GENERATED_KEYS när Ni förbereder satsen och läs sedan 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());
}
}
}Batchinfogningar
Det går långsamt att infoga tusentals rader med en sats i taget. Använd batchkörning:
- Bind parametrarna och anropa sedan
ps.addBatch(). - Upprepa detta för varje rad.
- Anropa
ps.executeBatch()en gång.
Detta skickar raderna tillsammans och minskar antalet resor fram och tillbaka avsevärt.
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());
}
}
}Återanvända ett Statement
En enda PreparedStatement kan köras många gånger med olika parametrar.
Bind platshållarna på nytt och anropa execute igen. Databasen återanvänder den cachade exekveringsplanen, vilket är anledningen till att förberedda satser är snabbare för återkommande frågor.
Konkatenera aldrig
Den gyllene regeln: alla värden som varierar måste vara en ?-parameter.
Endast fasta, kodstyrda identifierare (som ett vitlistat kolumnnamn) får konkateneras — och även då måste Ni validera mot en tillåtelselista. Data från användaren binds alltid och konkateneras aldrig.
Omfattning för stängning och återanvändning
En PreparedStatement är kopplad till sin Connection och bör stängas när den inte längre används.
Inuti try-with-resources deklarerar Ni satsen så att den stängs före anslutningen. Om Ni återanvänder en sats många gånger i en loop ska Ni förbereda den en gång utanför loopen och bara binda parametrarna på nytt inuti — om satsen förbereds på nytt vid varje iteration går den cachade planen förlorad.
Snabb kontroll
Varför förhindrar en PreparedStatement SQL-injektion?
Sammanfattning
Ni har lärt er att skriva säkra och snabba SQL-frågor:
- Använd platshållare med
?och typade setter-metoder (indexering från 1). - Bind värden med
setString,setInt,setNulloch så vidare. - Hämta automatiskt genererade nycklar med
RETURN_GENERATED_KEYSochgetGeneratedKeys. - Kör många skrivningar i batch med
addBatch+executeBatch. - Aldrig sammanfoga användarinmatning med SQL.
Lär dig Java med en AI-lärare – gratis
Skriv och kör riktig kod i webbläsaren, få omedelbar hjälp av en AI-lärare dygnet runt och fortsätt där du slutade – på webben eller i appen.
- Kurser
- 104
- Lektioner
- 374
Vanliga frågor
Är lektionen ”PreparedStatement” gratis?
Ja – hela texten till ”PreparedStatement” kan läsas gratis här på webben. Om Ni vill öva interaktivt med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt och låsa upp resten av kursen i Java Academy, kan Ni uppgradera till CoddyKit PRO. Kursen i Java Academy innehåller totalt 4 lektioner.
Vad lär jag mig i ”PreparedStatement”?
Säkra parameteriserade frågor. Ni övar på Java Academy med praktisk kod som körs direkt i webbläsaren, medan en AI-handledare som är tillgänglig dygnet runt svarar på Era frågor under lektionen.
Behöver jag någon erfarenhet för att börja lära mig Java Academy?
Du behöver inga förkunskaper. Utbildningen i Java Academy på CoddyKit är upplagd för allt från nybörjare till avancerade elever, så att du kan börja här eller från början och gå fram i din egen takt. Detta är lektion 2 av 4.
Hur lång tid tar lektionen ”PreparedStatement”?
De flesta CoddyKit-lektioner tar cirka 5–10 minuter. Varje lektion är kort och interaktiv, så att du gör stadiga framsteg och kan fortsätta precis där du slutade – på webben eller i appen.
Kan jag skriva och köra kod i den här Java Academy-lektionen?
Ja. Varje Java Academy-lektion innehåller en inbyggd kodredigerare, så att du kan skriva och köra riktig kod direkt i webbläsaren och få omedelbar AI-feedback – utan lokal installation.
Alla lektioner i den här kursen
- Grunderna i JDBC
- PreparedStatement
- Transaktioner
- Anslutningspoolning med HikariCP