Java Academy · Les

PreparedStatement

Veilige query's met parameters

Les 2 van 413 stappen

PreparedStatement is een gratis Java Academy-les op CoddyKit. Dit is les 2 van 4. Je kunt de volledige les hieronder gratis lezen en daarna in de browser praktisch oefenen met een ingebouwde code-editor en een AI-begeleider die 24/7 beschikbaar is. Deze les maakt deel uit van het leertraject Java Academy. Je voortgang wordt gesynchroniseerd op het web en in de CoddyKit-app. De cursus Java Academy bevat in totaal 4 lessen.

Waarom PreparedStatement?

SQL opbouwen door invoer van de gebruiker aaneen te schakelen is gevaarlijk en traag.

PreparedStatement lost beide problemen op:

  • Veiligheid — waarden worden afzonderlijk van de SQL verzonden, waardoor SQL-injectie wordt voorkomen.
  • Prestaties — de database kan de query één keer ontleden en plannen en deze daarna opnieuw gebruiken.

Het probleem van SQL-injectie

Stel dat je een query opbouwt door tekenreeksen aaneen te schakelen. Als een gebruiker '; DROP TABLE users; -- als naam invoert, voert de samengevoegde SQL dat uit als een opdracht.

Dit is SQL-injectie, een van de oudste en schadelijkste kwetsbaarheden op internet. PreparedStatement maakt dit door het ontwerp onmogelijk.

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);
    }
}

Plaatsaanduidingen

In een PreparedStatement schrijf je voor elke waarde die je later aanlevert een ?.

Daarna koppel je waarden met setters voor het juiste type:

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

Parameterindexen beginnen bij 1, in dezelfde volgorde als de ?-tekens.

Een veilige query

Maak de instructie met prepareStatement, koppel de parameters en roep daarna executeQuery aan.

De waarde komt nooit in aanraking met de SQL-tekst, dus injectie kan niet plaatsvinden, ongeacht wat de gebruiker invoert.

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());
        }
    }
}

Meerdere parameters koppelen

Elke ? krijgt een eigen aanroep van een setter. De volgorde is belangrijk: parameter 1 is de eerste ?, parameter 2 de tweede, enzovoort.

Gebruik de setter die overeenkomt met het kolomtype — setInt, setString, setDouble, setBoolean enzovoort.

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 verwerken

Gebruik setNull(index, type) met een constante van java.sql.Types om een SQL-NULL te koppelen.

Geef geen Java-null door aan setString in de hoop dat het werkt — wees expliciet:

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

Gegenereerde sleutels ophalen

Na een INSERT heb je vaak de automatisch gegenereerde id nodig.

Geef Statement.RETURN_GENERATED_KEYS door bij het voorbereiden en lees daarna 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());
        }
    }
}

Batchgewijs invoegen

Duizenden rijen één voor één invoegen is traag. Gebruik batchverwerking:

  • Koppel de parameters en roep daarna ps.addBatch() aan.
  • Herhaal dit voor elke rij.
  • Roep één keer ps.executeBatch() aan.

Hiermee verstuur je de rijen samen en verminder je het aantal netwerkritten drastisch.

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());
        }
    }
}

Een Statement hergebruiken

Eén PreparedStatement kan vaak worden uitgevoerd met verschillende parameters.

Koppel de plaatsaanduidingen opnieuw en roep execute nogmaals aan. De database hergebruikt het gecachte uitvoeringsplan. Daarom zijn voorbereide statements sneller bij herhaalde queries.

Nooit aaneenschakelen

De gouden regel: elke waarde die kan variëren moet een parameter met ? zijn.

Alleen vaste, door de code beheerde identifiers (zoals een kolomnaam op een goedkeuringslijst) mogen worden aaneengeschakeld — en zelfs dan moet je ze controleren aan de hand van een toestemmingslijst. Door de gebruiker aangeleverde gegevens worden altijd gekoppeld en nooit aaneengeschakeld.

Sluiten en bereik voor hergebruik

Een PreparedStatement is gekoppeld aan zijn Connection en moet na gebruik worden gesloten.

Declareer de instructie binnen try-with-resources, zodat deze vóór de verbinding wordt gesloten. Als je een instructie vaak opnieuw gebruikt in een lus, bereid je deze één keer buiten de lus voor en koppel je alleen binnen de lus de parameters opnieuw — bij elke herhaling opnieuw voorbereiden gooit het gecachte plan weg.

Korte controle

Waarom voorkomt een PreparedStatement SQL-injectie?

Samenvatting

Je hebt geleerd veilige, snelle query's te schrijven:

  • Gebruik ?-plaatshouders en setters met het juiste type (index vanaf 1).
  • Koppel waarden met setString, setInt, setNull, enzovoort.
  • Haal automatisch gegenereerde sleutels op met RETURN_GENERATED_KEYS en getGeneratedKeys.
  • Verwerk veel schrijfbewerkingen in één batch met addBatch + executeBatch.
  • Voeg gebruikersinvoer nooit samen met SQL.
Gratis beginnen

Leer Java met een AI-tutor — gratis

Schrijf echte code en voer die uit in je browser, krijg direct hulp van een AI-tutor die 24/7 beschikbaar is en ga verder waar je gebleven bent op het web of in de app.

Cursussen
104
Lessen
374

Veelgestelde vragen

Is de les “PreparedStatement” gratis?

Ja — de volledige tekst van “PreparedStatement” kun je hier gratis op het web lezen. Als je interactief wilt oefenen met een ingebouwde code-editor en een AI-begeleider die 24/7 beschikbaar is, en de rest van de cursus Java Academy wilt ontgrendelen, kun je upgraden naar CoddyKit PRO. De cursus Java Academy bevat in totaal 4 lessen.

Wat leer ik in “PreparedStatement”?

Veilige query's met parameters Je oefent met Java Academy door code rechtstreeks in de browser uit te voeren. Een AI-begeleider die 24/7 beschikbaar is beantwoordt je vragen terwijl je de les doorwerkt.

Heb ik ervaring nodig om met Java Academy te beginnen?

Ervaring vooraf is niet nodig. Java Academy op CoddyKit is opgebouwd voor beginners tot gevorderden, zodat je hier of bij het begin kunt starten en in je eigen tempo kunt leren. Dit is les 2 van 4.

Hoe lang duurt de les “PreparedStatement”?

De meeste lessen van CoddyKit duren ongeveer 5–10 minuten. Elke les is kort en interactief, zodat je gestaag vooruitgaat en op het web en in de app precies verdergaat waar je was gebleven.

Kan ik code schrijven en uitvoeren in deze les over Java Academy?

Ja. Elke les over Java Academy bevat een ingebouwde code-editor, zodat je rechtstreeks in je browser echte code kunt schrijven en uitvoeren en direct feedback van AI krijgt — lokale installatie is niet nodig.

Alle lessen in deze cursus

  1. De basis van JDBC
  2. PreparedStatement
  3. Transacties
  4. Connection pooling met HikariCP
← Terug naar Java Academy