Въпреки че е вярно, че в много случаи основният SQL израз ще свърши работата за много промени в базата данни или заявки, това често е най-добра практика за да се възползвате от гъвкавостта и предимствата, предоставени ви с помощта на PreparedStatements .
Основните разлики между стандартен JDBC израз и PreparedStatement се определят най-добре от ползите че PreparedStatement предоставя на вас и вашето приложение. По-долу ще разгледаме трите основни предимства на PreparedStatements над обикновени JDBC/SQL оператори.
Предотвратяване на инжектиране на SQL
Първата полза от използването на PreparedStatement е, че можете да се възползвате от множеството .setXYZ() методи, като .setString() , което позволява на вашия код автоматично да избягва специални символи като кавички в рамките на предадения в SQL израз, предотвратявайки винаги опасното SQL injection атака.
Например, в стандартен SQL израз може да е типично да се вмъкнат стойности директно в рамките на израза, като така:
statement = "INSERT INTO books (title, primary_author, published_date) VALUES ('" + book.getTitle() + "', '" + book.getPrimaryAuthor() + "', '" + new Timestamp(book.getPublishedDate().getTime()) + "'";
Това ще ви принуди да изпълните свой собствен код, за да предотвратите SQL инжекции, като избягвате кавички и други специални знаци от вмъкнатите стойности.
Обратно, PreparedStatement може да бъде извикан както следва, като се използва .setXYZ() методи за вмъкване на стойности с автоматично екраниране на символи по време на изпълнение на метода:
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();
Предварителна компилация
Друго предимство на PreparedStatement е, че самият SQL е pre-compiled един път и след това се запазва в паметта от системата, вместо да се компилира всеки път, когато операторът се извиква. Това позволява по-бързо изпълнение, особено когато PreparedStatement се използва във връзка с batches , които ви позволяват да изпълните серия (или batch ) на SQL изрази наведнъж по време на една връзка с база данни.
Например, тук имаме функция, която приема List на книги. За всяка book в списъка искаме да изпълним INSERT изявление, но ще ги добавим всички към партида от PreparedStatements и да ги изпълни с един замах:
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();
}
}
Вмъкване на необичайни типове данни в SQL изявление
Последното предимство на PreparedStatements което ще покрием е възможността за вмъкване на необичайни типове данни в самия SQL израз, като Timestamp , InputStream , и много други.
Например, можем да използваме PreparedStatement за да добавите снимка на корицата към записа на нашата книга с помощта на .setBinaryStream() метод:
ps = connection.prepareStatement("INSERT INTO books (cover_photo) VALUES (?)");
ps.setBinaryStream(1, book.getPhoto());
ps.executeUpdate();