Въпреки че е вярно, че в много случаи основният 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();