Chociaż prawdą jest, że w wielu przypadkach podstawowe polecenie SQL wykona zadanie dla wielu zmian lub zapytań w bazie danych, często jest to najlepsza praktyka aby wykorzystać elastyczność i korzyści, jakie daje Ci użycie PreparedStatements
.
Podstawowe różnice między standardową instrukcją JDBC a PreparedStatement
najlepiej określają korzyści że PreparedStatement
zapewnia Tobie i Twojej aplikacji. Poniżej przeanalizujemy trzy podstawowe zalety PreparedStatements
nad zwykłymi instrukcjami JDBC/SQL.
Zapobieganie wstrzykiwaniu SQL
Pierwsza korzyść z używania PreparedStatement
możesz skorzystać z mnogości .setXYZ()
metody, takie jak .setString()
, który pozwala Twojemu kodowi na automatyczne unikanie znaków specjalnych, takich jak cytaty w ramach przekazanej instrukcji SQL, zapobiegając zawsze niebezpiecznemu SQL injection
atak.
Na przykład w standardowej instrukcji SQL typowe może być wstawianie wartości bezpośrednio w instrukcji, na przykład:
statement = "INSERT INTO books (title, primary_author, published_date) VALUES ('" + book.getTitle() + "', '" + book.getPrimaryAuthor() + "', '" + new Timestamp(book.getPublishedDate().getTime()) + "'";
To zmusiłoby Cię do wykonania własnego kodu, aby zapobiec wstrzykiwaniu SQL poprzez unikanie cudzysłowów i innych znaków specjalnych z wstawionych wartości.
I odwrotnie, PreparedStatement
można wywołać w następujący sposób, używając .setXYZ()
metody do wstawiania wartości z automatycznym unikaniem znaków podczas wykonywania metody:
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();
Wstępna kompilacja
Kolejna zaleta PreparedStatement
jest to, że sam SQL jest pre-compiled
jednorazowo, a następnie zachowywane w pamięci przez system, zamiast być kompilowane za każdym razem, gdy instrukcja jest wywoływana. Pozwala to na szybsze wykonanie, szczególnie gdy PreparedStatement
jest używany w połączeniu z batches
, które umożliwiają wykonanie serii (lub batch
) wszystkich instrukcji SQL jednocześnie podczas jednego połączenia z bazą danych.
Na przykład tutaj mamy funkcję, która akceptuje List
książek. Dla każdej book
na liście chcemy wykonać INSERT
oświadczenie, ale zamierzamy dodać je wszystkie do partii PreparedStatements
i wykonaj je wszystkie za jednym zamachem:
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();
}
}
Wstawianie nieprawidłowych typów danych w instrukcji SQL
Ostatnia zaleta PreparedStatements
omówimy to możliwość wstawiania nieprawidłowych typów danych do samego wyrażenia SQL, takich jak Timestamp
, InputStream
i wiele innych.
Na przykład możemy użyć PreparedStatement
aby dodać zdjęcie na okładkę do naszego rekordu książki za pomocą .setBinaryStream()
metoda:
ps = connection.prepareStatement("INSERT INTO books (cover_photo) VALUES (?)");
ps.setBinaryStream(1, book.getPhoto());
ps.executeUpdate();