Database
 sql >> Database >  >> RDS >> Database

La differenza tra una dichiarazione JDBC e una dichiarazione preparata

Sebbene sia vero che, in molti casi, un'istruzione SQL di base farà il lavoro per molte modifiche o query del database, spesso è una best practice per sfruttare la flessibilità e i vantaggi offerti dall'utilizzo di PreparedStatements .

Le principali differenze tra un'istruzione JDBC standard e un PreparedStatement sono meglio definiti dai vantaggi che un PreparedStatement offre te e la tua applicazione. Di seguito esamineremo i tre vantaggi principali di PreparedStatements su normali istruzioni JDBC/SQL.

Prevenzione iniezione SQL

Il primo vantaggio dell'utilizzo di un PreparedStatement puoi sfruttare la moltitudine di .setXYZ() metodi, come .setString() , che consente al codice di eseguire automaticamente l'escape di caratteri speciali come le virgolette all'interno dell'istruzione SQL passata, prevenendo la sempre pericolosa SQL injection attacco.

Ad esempio, in un'istruzione SQL standard, può essere tipico inserire valori direttamente in linea con l'istruzione, in questo modo:

statement = "INSERT INTO books (title, primary_author, published_date) VALUES ('" + book.getTitle() + "', '" + book.getPrimaryAuthor() + "', '" + new Timestamp(book.getPublishedDate().getTime()) + "'";

Ciò ti costringerebbe a eseguire il tuo codice per impedire le iniezioni SQL eseguendo l'escape di virgolette e altri caratteri speciali dai valori inseriti.

Al contrario, un PreparedStatement potrebbe essere invocato come segue, usando .setXYZ() metodi per inserire valori con caratteri di escape automatico durante l'esecuzione del metodo:

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

Pre-compilazione

Un altro vantaggio di un PreparedStatement è che l'SQL stesso è pre-compiled una sola volta e quindi mantenuto in memoria dal sistema, anziché essere compilato ogni volta che viene chiamata l'istruzione. Ciò consente un'esecuzione più rapida, in particolare quando un PreparedStatement viene utilizzato insieme a batches , che ti consentono di eseguire una serie (o batch ) di istruzioni SQL contemporaneamente durante una singola connessione al database.

Ad esempio, qui abbiamo una funzione che accetta un List di libri. Per ogni book nell'elenco, vogliamo eseguire un INSERT istruzione, ma li aggiungeremo tutti a un batch di PreparedStatements ed eseguili tutti in un colpo solo:

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

Inserimento di tipi di dati anomali nell'istruzione SQL

Il vantaggio finale di PreparedStatements che tratteremo è la possibilità di inserire tipi di dati anomali nell'istruzione SQL stessa, come Timestamp , InputStream e molti altri.

Ad esempio, possiamo utilizzare un PreparedStatement per aggiungere una foto di copertina al record del nostro libro utilizzando .setBinaryStream() metodo:

ps = connection.prepareStatement("INSERT INTO books (cover_photo) VALUES (?)");
ps.setBinaryStream(1, book.getPhoto());
ps.executeUpdate();