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