Även om det är sant att en grundläggande SQL-sats i många fall kommer att få jobbet gjort för många databasändringar eller frågor, är det ofta en bästa praxis att använda den flexibilitet och fördelar som du får genom att använda PreparedStatements
.
De primära skillnaderna mellan en standard JDBC-sats och en PreparedStatement
definieras bäst av fördelarna att en PreparedStatement
ger dig och din ansökan. Nedan undersöker vi de tre kärnfördelarna med PreparedStatements
över vanliga JDBC/SQL-satser.
SQL Injection Prevention
Den första fördelen med att använda en PreparedStatement
är att du kan dra nytta av mängden .setXYZ()
metoder, såsom .setString()
, som gör att din kod automatiskt kan undkomma specialtecken som citattecken inom den godkända SQL-satsen, vilket förhindrar den alltid farliga SQL injection
attack.
Till exempel, i en standard SQL-sats kan det vara typiskt att infoga värden direkt i satsen, som så:
statement = "INSERT INTO books (title, primary_author, published_date) VALUES ('" + book.getTitle() + "', '" + book.getPrimaryAuthor() + "', '" + new Timestamp(book.getPublishedDate().getTime()) + "'";
Detta skulle tvinga dig att köra din egen kod för att förhindra SQL-injektioner genom att undvika citattecken och andra specialtecken från de infogade värdena.
Omvänt, en PreparedStatement
kan anropas enligt följande med .setXYZ()
metoder för att infoga värden med automatiskt tecken escape under metodkörning:
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();
Förkompilering
En annan fördel med en PreparedStatement
är att själva SQL är pre-compiled
en enda gång och sedan bevaras i minnet av systemet, istället för att kompileras varje gång uttalandet anropas. Detta möjliggör snabbare exekvering, särskilt när en PreparedStatement
används tillsammans med batches
, som låter dig köra en serie (eller batch
) av SQL-satser på en gång under en enda databasanslutning.
Här har vi till exempel en funktion som accepterar en List
av böcker. För varje book
i listan vill vi köra en INSERT
uttalande, men vi kommer att lägga till alla i en sats av PreparedStatements
och avrätta dem alla i ett svep:
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();
}
}
Infogande av onormala datatyper i SQL-sats
Den sista fördelen med PreparedStatements
som vi kommer att täcka är möjligheten att infoga onormala datatyper i själva SQL-satsen, såsom Timestamp
, InputStream
, och många fler.
Till exempel kan vi använda en PreparedStatement
för att lägga till ett omslagsfoto till vår bokföring med hjälp av .setBinaryStream()
metod:
ps = connection.prepareStatement("INSERT INTO books (cover_photo) VALUES (?)");
ps.setBinaryStream(1, book.getPhoto());
ps.executeUpdate();