Diferencia entre declaración y declaración preparada

Parte superior de Java

Acabo de anunciar el nuevo curso Learn Spring , centrado en los fundamentos de Spring 5 y Spring Boot 2:

>> VER EL CURSO

1. Información general

En este tutorial, exploraremos las diferencias entre las interfaces Statement y PreparedStatement de JDBC . No cubriremos CallableStatement , una interfaz API de JDBC que se utiliza para ejecutar procedimientos almacenados.

2. Interfaz API JDBC

Tanto Statement como PreparedStatement se pueden utilizar para ejecutar consultas SQL. Estas interfaces se ven muy similares. Sin embargo, difieren significativamente entre sí en características y rendimiento:

  • Declaración : se utiliza para ejecutar consultas SQL basadas en cadenas
  • PreparedStatement : se utiliza para ejecutar consultas SQL parametrizadas

Para poder usar Statement y PreparedStatement en nuestros ejemplos, declararemos el conector h2 JDBC como una dependencia en nuestro archivo pom.xml :

 com.h2database h2 1.4.200 

Definamos una entidad que usaremos a lo largo de este artículo:

public class PersonEntity { private int id; private String name; // standard setters and getters }

3. Declaración

En primer lugar, la interfaz Statement acepta cadenas como consultas SQL. Por lo tanto, el código se vuelve menos legible cuando concatenamos cadenas SQL:

public void insert(PersonEntity personEntity) { String query = "INSERT INTO persons(id, name) VALUES(" + personEntity.getId() + ", '" + personEntity.getName() + "')"; Statement statement = connection.createStatement(); statement.executeUpdate(query); }

En segundo lugar, es vulnerable a la inyección de SQL . Los siguientes ejemplos ilustran esta debilidad.

En la primera línea, la actualización establecerá la columna " nombre " en todas las filas como " hacker ", ya que cualquier cosa después de "-" se interpreta como un comentario en SQL y las condiciones de la declaración de actualización se ignorarán. En la segunda línea, la inserción fallará porque la cita en la columna " nombre " no se ha escapado:

dao.update(new PersonEntity(1, "hacker' --")); dao.insert(new PersonEntity(1, "O'Brien"))

En tercer lugar, JDBC pasa la consulta con valores en línea a la base de datos . Por lo tanto, no hay optimización de consultas y, lo más importante, el motor de la base de datos debe garantizar todas las comprobaciones . Además, la consulta no aparecerá igual en la base de datos y evitará el uso de la caché . Del mismo modo, las actualizaciones por lotes deben ejecutarse por separado:

public void insert(List personEntities) { for (PersonEntity personEntity: personEntities) { insert(personEntity); } }

En cuarto lugar, la interfaz Statement es adecuada para consultas DDL como CREATE, ALTER y DROP :

public void createTables() { String query = "create table if not exists PERSONS (ID INT, NAME VARCHAR(45))"; connection.createStatement().executeUpdate(query); }

Finalmente, la interfaz Statement no se puede utilizar para almacenar y recuperar archivos y matrices .

4. Declaración preparada

En primer lugar, PreparedStatement amplía la interfaz de Statement . Tiene métodos para vincular varios tipos de objetos , incluidos archivos y matrices. Por lo tanto, el código se vuelve fácil de entender :

public void insert(PersonEntity personEntity) { String query = "INSERT INTO persons(id, name) VALUES( ?, ?)"; PreparedStatement preparedStatement = connection.prepareStatement(query); preparedStatement.setInt(1, personEntity.getId()); preparedStatement.setString(2, personEntity.getName()); preparedStatement.executeUpdate(); }

En segundo lugar, protege contra la inyección de SQL , al escapar del texto para todos los valores de parámetros proporcionados:

@Test void whenInsertAPersonWithQuoteInText_thenItNeverThrowsAnException() { assertDoesNotThrow(() -> dao.insert(new PersonEntity(1, "O'Brien"))); } @Test void whenAHackerUpdateAPerson_thenItUpdatesTheTargetedPerson() throws SQLException { dao.insert(Arrays.asList(new PersonEntity(1, "john"), new PersonEntity(2, "skeet"))); dao.update(new PersonEntity(1, "hacker' --")); List result = dao.getAll(); assertEquals(Arrays.asList( new PersonEntity(1, "hacker' --"), new PersonEntity(2, "skeet")), result); }

En tercer lugar, el PreparedStatement utiliza una compilación previa . Tan pronto como la base de datos reciba una consulta, comprobará la caché antes de compilar previamente la consulta. En consecuencia, si no se almacena en caché, el motor de la base de datos lo guardará para el próximo uso.

Además, esta función acelera la comunicación entre la base de datos y la JVM a través de un protocolo binario que no es SQL. Es decir, hay menos datos en los paquetes, por lo que la comunicación entre los servidores es más rápida.

En cuarto lugar, PreparedStatement proporciona una ejecución por lotes durante una sola conexión a la base de datos . Veamos esto en acción:

public void insert(List personEntities) throws SQLException { String query = "INSERT INTO persons(id, name) VALUES( ?, ?)"; PreparedStatement preparedStatement = connection.prepareStatement(query); for (PersonEntity personEntity: personEntities) { preparedStatement.setInt(1, personEntity.getId()); preparedStatement.setString(2, personEntity.getName()); preparedStatement.addBatch(); } preparedStatement.executeBatch(); }

A continuación, PreparedStatement proporciona una forma sencilla de almacenar y recuperar archivos mediante el uso de tipos de datos BLOB y CLOB . En la misma línea, ayuda a almacenar listas al convertir java.sql.Array en un SQL Array.

Por último, PreparedStatement implementa métodos como getMetadata () que contienen información sobre el resultado devuelto.

5. Conclusión

En este tutorial, presentamos las principales diferencias entre PreparedStatement y Statement . Ambas interfaces ofrecen métodos para ejecutar consultas SQL, pero es más adecuado utilizar Statement para consultas DDL y PreparedStatement para consultas DML.

Como de costumbre, todos los ejemplos de código están disponibles en GitHub.

Fondo de Java

Acabo de anunciar el nuevo curso Learn Spring , centrado en los fundamentos de Spring 5 y Spring Boot 2:

>> VER EL CURSO