Java Academy · Lektion

PreparedStatement

Säkra parameteriserade frågor.

Lektion 2 av 413 steg

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, setNull och så vidare.
  • Hämta automatiskt genererade nycklar med RETURN_GENERATED_KEYS och getGeneratedKeys.
  • Kör många skrivningar i batch med addBatch + executeBatch.
  • Aldrig sammanfoga användarinmatning med SQL.
Gratis att börja

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

  1. Grunderna i JDBC
  2. PreparedStatement
  3. Transaktioner
  4. Anslutningspoolning med HikariCP
← Tillbaka till Java Academy