Hoewel het waar is dat in veel gevallen een eenvoudige SQL-instructie de klus zal klaren voor veel databasewijzigingen of query's, is het vaak een best practice om gebruik te maken van de flexibiliteit en voordelen die u worden geboden door gebruik te maken van PreparedStatements
.
De belangrijkste verschillen tussen een standaard JDBC-statement en een PreparedStatement
worden het best gedefinieerd door de voordelen dat een PreparedStatement
biedt u en uw toepassing. Hieronder onderzoeken we de drie belangrijkste voordelen van PreparedStatements
over reguliere JDBC/SQL-statements.
SQL-injectiepreventie
Het eerste voordeel van het gebruik van een PreparedStatement
is dat u kunt profiteren van de veelheid aan .setXYZ()
methoden, zoals .setString()
, waarmee uw code automatisch kan ontsnappen aan speciale tekens zoals aanhalingstekens binnen de doorgegeven SQL-instructie, waardoor de altijd gevaarlijke SQL injection
wordt voorkomen aanval.
In een standaard SQL-statement kan het bijvoorbeeld gebruikelijk zijn om waarden direct inline met het statement in te voegen, zoals:
statement = "INSERT INTO books (title, primary_author, published_date) VALUES ('" + book.getTitle() + "', '" + book.getPrimaryAuthor() + "', '" + new Timestamp(book.getPublishedDate().getTime()) + "'";
Dit zou u dwingen uw eigen code uit te voeren om SQL-injecties te voorkomen door aanhalingstekens en andere speciale tekens uit de ingevoegde waarden te laten ontsnappen.
Omgekeerd, een PreparedStatement
kan als volgt worden aangeroepen, met behulp van de .setXYZ()
methoden om waarden in te voegen met automatische escapetekens tijdens de uitvoering van de methode:
ps = connection.prepareStatement("INSERT INTO books (title, primary_author, published_date) VALUES (?, ?, ?)");
ps.setString(1, book.getTitle());
ps.setString(2, book.getPrimaryAuthor());
ps.setTimestamp(3, new Timestamp(book.getPublishedDate().getTime()));
ps.executeUpdate();
Pre-compilatie
Nog een voordeel van een PreparedStatement
is dat de SQL zelf pre-compiled
. is een enkele keer en vervolgens door het systeem in het geheugen bewaard, in plaats van elke keer dat de instructie wordt aangeroepen, te worden gecompileerd. Dit zorgt voor een snellere uitvoering, vooral wanneer een PreparedStatement
wordt gebruikt in combinatie met batches
, waarmee u een reeks . kunt uitvoeren (of batch
) van SQL-instructies tegelijk tijdens een enkele databaseverbinding.
Hier hebben we bijvoorbeeld een functie die een List
. accepteert van boeken. Voor elk book
in de lijst willen we een INSERT
. uitvoeren statement, maar we gaan ze allemaal toevoegen aan een batch van PreparedStatements
en voer ze allemaal in één klap uit:
public void createBooks(List<Entity> books) throws SQLException {
try (
Connection connection = dataSource.getConnection();
PreparedStatement ps = connection.prepareStatement("INSERT INTO books (title, primary_author, published_date) VALUES (?, ?, ?)");
) {
for (Entity book : books) {
ps.setString(1, book.getTitle());
ps.setString(2, book.getPrimaryAuthor());
ps.setTimestamp(3, new Timestamp(book.getPublishedDate().getTime()));
ps.addBatch();
}
ps.executeBatch();
}
}
Invoeging van abnormale gegevenstypen in SQL-instructie
Het laatste voordeel van PreparedStatements
die we zullen behandelen, is de mogelijkheid om abnormale gegevenstypen in de SQL-instructie zelf in te voegen, zoals Timestamp
, InputStream
, en nog veel meer.
We kunnen bijvoorbeeld een PreparedStatement
. gebruiken om een omslagfoto toe te voegen aan ons boekverslag met behulp van de .setBinaryStream()
methode:
ps = connection.prepareStatement("INSERT INTO books (cover_photo) VALUES (?)");
ps.setBinaryStream(1, book.getPhoto());
ps.executeUpdate();