Diferencia entre la declaración y la declaración preparada

222

La Declaración preparada es una versión un poco más poderosa de una Declaración, y siempre debe ser al menos tan rápida y fácil de manejar como una Declaración.
La declaración preparada puede ser parametrizada

La mayoría de las bases de datos relacionales manejan una consulta JDBC / SQL en cuatro pasos:

  1. Analizar la consulta SQL entrante
  2. Compile la consulta SQL
  3. Planifique / optimice la ruta de adquisición de datos
  4. Ejecute la consulta / adquisición optimizada y devuelva datos

Una declaración siempre procederá a través de los cuatro pasos anteriores para cada consulta SQL enviada a la base de datos. Una declaración preparada ejecuta previamente los pasos (1) - (3) en el proceso de ejecución anterior. Por lo tanto, al crear una declaración preparada, se realiza una optimización previa de inmediato. El efecto es disminuir la carga en el motor de la base de datos en el momento de la ejecución.

Ahora mi pregunta es: "¿Hay alguna otra ventaja de usar la Declaración preparada?"

CodeBee ..
fuente
12
el más eficiente según mi opinión es que su consulta puede ser parametrizada dinámicamente
Hussain Akhtar Wahid 'Ghouri'

Respuestas:

198

Ventajas de a PreparedStatement:

  • La precompilación y el almacenamiento en caché del lado DB de la instrucción SQL conduce a una ejecución general más rápida y a la capacidad de reutilizar la misma instrucción SQL en lotes .

  • Prevención automática de ataques de inyección SQL mediante escape integrado de comillas y otros caracteres especiales. Tenga en cuenta que esto requiere que utilice cualquiera de los métodos para establecer los valoresPreparedStatement setXxx()

    preparedStatement = connection.prepareStatement("INSERT INTO Person (name, email, birthdate, photo) VALUES (?, ?, ?, ?)");
    preparedStatement.setString(1, person.getName());
    preparedStatement.setString(2, person.getEmail());
    preparedStatement.setTimestamp(3, new Timestamp(person.getBirthdate().getTime()));
    preparedStatement.setBinaryStream(4, person.getPhoto());
    preparedStatement.executeUpdate();

    y, por lo tanto , no alinee los valores en la cadena SQL concatenando cadenas.

    preparedStatement = connection.prepareStatement("INSERT INTO Person (name, email) VALUES ('" + person.getName() + "', '" + person.getEmail() + "'");
    preparedStatement.executeUpdate();
  • Facilita la configuración de los objetos de Java no estándar en una cadena SQL, por ejemplo Date, Time, Timestamp, BigDecimal, InputStream( Blob) y Reader( Clob). En la mayoría de esos tipos no puede "simplemente" hacer toString()lo que haría de una manera simple Statement. Incluso podría refactorizarlo todo para usarlo PreparedStatement#setObject()dentro de un ciclo como se demuestra en el siguiente método de utilidad:

    public static void setValues(PreparedStatement preparedStatement, Object... values) throws SQLException {
        for (int i = 0; i < values.length; i++) {
            preparedStatement.setObject(i + 1, values[i]);
        }
    }

    Que se puede usar de la siguiente manera:

    preparedStatement = connection.prepareStatement("INSERT INTO Person (name, email, birthdate, photo) VALUES (?, ?, ?, ?)");
    setValues(preparedStatement, person.getName(), person.getEmail(), new Timestamp(person.getBirthdate().getTime()), person.getPhoto());
    preparedStatement.executeUpdate();
BalusC
fuente
44
Un texto descriptivo y explicativo, junto con referencias y ejemplos, es una excelente respuesta. +1
XenoRo
1
@RD Esto puede ser cierto porque una declaración preparada requiere 2 viajes de ida y vuelta a la base de datos: el primero para preparar, el segundo para ejecutar. Sin embargo, lo probaría. Supongo que el plan todavía se almacenaría en caché en el servidor de base de datos durante un Statement, pero puede valer la pena una prueba.
Brandon
2
No puedo decir con certeza con Java, pero en general una declaración preparada no preforma "escapar de comillas y otros caracteres especiales"; en su lugar, realiza la separación del SQL ejecutable y los datos , enviando los parámetros al DBMS como paquetes de información separados después de que el SQL se haya convertido en un plan de consulta.
IMSoP
@BalusC - Gracias por una explicación detallada.
CodeBee ..
49
  1. Están precompilados (una vez), por lo que son más rápidos para la ejecución repetida de SQL dinámico (donde los parámetros cambian)

  2. El almacenamiento en caché de instrucciones de la base de datos aumenta el rendimiento de ejecución de la base de datos

    Las bases de datos almacenan cachés de planes de ejecución para declaraciones ejecutadas previamente. Esto permite que el motor de la base de datos reutilice los planes para las declaraciones que se han ejecutado anteriormente. Debido a que PreparedStatement usa parámetros, cada vez que se ejecuta aparece como el mismo SQL, la base de datos puede reutilizar el plan de acceso anterior, lo que reduce el procesamiento. Las declaraciones "en línea" los parámetros en la cadena SQL y, por lo tanto, no aparecen como el mismo SQL en la base de datos, lo que impide el uso de caché.

  3. El protocolo de comunicaciones binarias significa menos ancho de banda y llamadas de comunicación más rápidas al servidor DB

    Las declaraciones preparadas normalmente se ejecutan a través de un protocolo binario no SQL. Esto significa que hay menos datos en los paquetes, por lo que las comunicaciones con el servidor son más rápidas. Como regla general, las operaciones de red son un orden de magnitud más lento que las operaciones de disco, que son un orden de magnitud más lento que las operaciones de CPU en memoria. Por lo tanto, cualquier reducción en la cantidad de datos enviados a través de la red tendrá un buen efecto en el rendimiento general.

  4. Protegen contra la inyección de SQL, escapando texto para todos los valores de parámetros proporcionados.

  5. Proporcionan una separación más fuerte entre el código de la consulta y los valores de los parámetros (en comparación con las cadenas SQL concatenadas), aumentan la legibilidad y ayudan a los mantenedores de códigos a comprender rápidamente las entradas y salidas de la consulta.

  6. En java, puede llamar a getMetadata () y getParameterMetadata () para reflexionar sobre los campos del conjunto de resultados y los campos de parámetros, respectivamente

  7. En java, acepta de forma inteligente los objetos java como tipos de parámetros a través de setObject, setBoolean, setByte, setDate, setDouble, setDouble, setFloat, setInt, setLong, setShort, setTime, setTimestamp: se convierte en formato de tipo JDBC que es comprensible para DB (no solo para String) () formato).

  8. En java, acepta los ARRAY SQL, como tipo de parámetro mediante el método setArray

  9. En java, acepta CLOBs, BLOBs, OutputStreams y Readers como parámetros "feeds" a través de los métodos setClob / setNClob, setBlob, setBinaryStream, setCharacterStream / setAsciiStream / setNCharacterStream, respectivamente

  10. En java, permite establecer valores específicos de DB para SQL DATALINK, SQL ROWID, SQL XML y NULL a través de los métodos setURL, setRowId, setSQLXML y setNull

  11. En java, hereda todos los métodos de Statement. Hereda el método addBatch y, además, permite agregar un conjunto de valores de parámetros para que coincida con el conjunto de comandos SQL por lotes a través del método addBatch.

  12. En java, un tipo especial de PreparedStatement (la subclase CallableStatement) permite la ejecución de procedimientos almacenados, lo que admite un alto rendimiento, encapsulación, programación de procedimientos y SQL, administración / mantenimiento / ajuste de DB de lógica y uso de lógica y características de DB patentadas

Glen Best
fuente
¿Cómo son posibles todas esas maravillas cuando ambas son solo interfaces?!?!
Rafael
1
Las 'maravillas' son posibles a través de métodos estándar de fábrica que devuelven implementaciones (específicas del proveedor) de las interfaces: Connection.createStatement y Connection.prepareStatement. Este diseño lo obliga a trabajar contra interfaces, por lo que no necesita conocer las clases de implementación específicas y evitar un acoplamiento estrecho innecesario con tales clases de implementación. Todo explicado con ejemplos en los documentos Java jdbc y documentos Java. :)
Glen Best
Su parte "como regla general" no tiene sentido, no es al revés 🤔
bhathiya-perera
38

PreparedStatementes una muy buena defensa (pero no infalible) para prevenir ataques de inyección SQL . La vinculación de los valores de los parámetros es una buena manera de protegerse contra "pequeñas Bobby Tables" que realizan una visita no deseada.

duffymo
fuente
66
¿Cómo se realizaría una inyección SQL a través de una declaración preparada entonces?
Michael Borgwardt
2
Michael, el controlador JDBC escapará automáticamente a las variables pasadas como argumentos a las declaraciones preparadas.
CodeBee ..
3
¿Puede dar un ejemplo de cómo un ataque de inyección SQL funcionaría contra una declaración preparada? ¿Asume un error en el código de la base de datos?
Peter Recore
2
Sí, pero es mucho más que "bastante tonto". Es una estupidez alucinante. Nadie con una pizca de conocimiento haría eso.
duffymo
2
Además, muchos proveedores de bases de datos no son compatibles con la parametrización de los nombres de columna (creo ORDER BY) y / o constantes numéricas en ciertos lugares (pensar LIMIT, OFFSETy otras soluciones de paginación), por lo que estos pueden ser atacados por inyección de SQL, incluso cuando Sentencias preparadas y parametrización se utiliza siempre que sea posible.
dnet
31

Algunos de los beneficios de PreparedStatement over Statement son:

  1. PreparedStatement nos ayuda a prevenir ataques de inyección SQL porque escapa automáticamente a los caracteres especiales.
  2. PreparedStatement nos permite ejecutar consultas dinámicas con entradas de parámetros.
  3. PreparedStatement proporciona diferentes tipos de métodos de establecimiento para establecer los parámetros de entrada para la consulta.
  4. PreparedStatement es más rápido que el estado de cuenta. Se vuelve más visible cuando reutilizamos PreparedStatement o utilizamos sus métodos de procesamiento por lotes para ejecutar múltiples consultas.
  5. PreparedStatement nos ayuda a escribir código orientado a objetos con métodos setter mientras que con Statement tenemos que usar String Concatenation para crear la consulta. Si hay varios parámetros para establecer, escribir consultas usando la concatenación de cadenas se ve muy feo y propenso a errores.

Lea más sobre el problema de inyección SQL en http://www.journaldev.com/2489/jdbc-statement-vs-preparedstatement-sql-injection-example

Pankaj
fuente
Leí tu artículo, muy bueno. Mi pregunta ahora es ¿por qué alguien usaría la Declaración? incluso para una consulta estática?
pedram bashiri
Siempre uso PreparedStatement, no conozco ningún escenario específico en el que la Declaración pueda tener más beneficios.
Pankaj
13

no hay mucho que agregar

1: si desea ejecutar una consulta en un bucle (más de 1 vez), la instrucción preparada puede ser más rápida debido a la optimización que mencionó.

2: la consulta parametrizada es una buena manera de evitar la inyección de SQL. Las consultas parametrizadas solo están disponibles en PreparedStatement.

mhshams
fuente
10

La declaración es estática y la declaración preparada es dinámica.

La declaración es adecuada para DDL y la declaración preparada para DML.

La declaración es más lenta mientras que la declaración preparada es más rápida.

más diferencias (archivado)

sandeep vanama
fuente
7

No se pueden hacer CLOB en una declaración.

Y: (OraclePreparedStatement) ps

orbfish
fuente
7

Citado por mattjames

El uso de una declaración en JDBC debe estar 100% localizado para ser usado para DDL (ALTER, CREATE, GRANT, etc.) ya que estos son los únicos tipos de declaración que no pueden aceptar VARIABLES BIND. PreparedStatements o CallableStatements deben usarse para CUALQUIER OTRO tipo de declaración (DML, Consultas). Como estos son los tipos de enunciados que aceptan variables de enlace.

Este es un hecho, una regla, una ley: use declaraciones preparadas POR TODAS PARTES. Use DECLARACIONES casi en ningún lado.

Raíz
fuente
5

la inyección preparada ignora la inyección sql, por lo que la seguridad aumenta en la declaración preparada

ashish geol
fuente
4
  • Es mas facil de leer
  • Puede convertir fácilmente la cadena de consulta en una constante
nanda
fuente
4

La instrucción se usará para ejecutar instrucciones SQL estáticas y no puede aceptar parámetros de entrada.

PreparedStatement se usará para ejecutar sentencias SQL muchas veces dinámicamente. Aceptará parámetros de entrada.

MARA MP
fuente
4

Otra característica de la consulta preparada o parametrizada: referencia tomada de este artículo.

Esta declaración es una de las características del sistema de base de datos en la que la misma declaración SQL se ejecuta repetidamente con alta eficiencia. Las declaraciones preparadas son un tipo de plantilla y son utilizadas por aplicaciones con diferentes parámetros.

La plantilla de declaración se prepara y se envía al sistema de base de datos y el sistema de base de datos realiza el análisis, la compilación y la optimización de esta plantilla y la almacena sin ejecutarla.

Algunos parámetros como, cuando la cláusula no se pasa durante la aplicación posterior de creación de plantilla, envían estos parámetros al sistema de base de datos y la plantilla de uso de la base de datos de la Declaración SQL y se ejecuta según la solicitud.

Las declaraciones preparadas son muy útiles contra la inyección de SQL porque la aplicación puede preparar parámetros usando diferentes técnicas y protocolos.

Cuando el número de datos aumenta y los índices cambian con frecuencia en ese momento, las declaraciones preparadas pueden fallar porque en esta situación se requiere un nuevo plan de consulta.

Anvesh
fuente
3

Statement la interfaz ejecuta sentencias SQL estáticas sin parámetros

PreparedStatement La interfaz (declaración de extensión) ejecuta una declaración SQL precompilada con / sin parámetros

  1. Eficiente para ejecuciones repetidas

  2. Está precompilado por lo que es más rápido.

Bernardo
fuente
2

No te confundas: simplemente recuerda

  1. La instrucción se usa para consultas estáticas como DDL, es decir, crear, descartar, alterar y preparar. La declaración se usa para consultas dinámicas, es decir, consulta DML.
  2. En Statement, la consulta no se precompila mientras que en prepareStatement la consulta se precompila, debido a que prepareStatement es eficiente en el tiempo.
  3. prepareStatement toma argumentos en el momento de la creación, mientras que Statement no toma argumentos. Por ejemplo, si desea crear una tabla e insertar un elemento, entonces :: Cree una tabla (estática) usando la instrucción y el elemento Insertar (dinámico) usando prepareStatement.
Roopam
fuente
1
prepareStatement toma argumentos en el momento de la creación, mientras que Statement no toma argumentos.
1

Seguí todas las respuestas de esta pregunta para cambiar un código heredado que funcionaba usando Statement(pero con inyecciones SQL) a una solución que usaba PreparedStatementun código mucho más lento debido a la poca comprensión de la semántica en torno a Statement.addBatch(String sql)& PreparedStatement.addBatch().

Así que estoy enumerando mi escenario aquí para que otros no cometan el mismo error.

Mi escenario era

Statement statement = connection.createStatement();

for (Object object : objectList) {
    //Create a query which would be different for each object 
    // Add this query to statement for batch using - statement.addBatch(query);
}
statement.executeBatch();

Entonces, en el código anterior, tuve miles de consultas diferentes, todas agregadas a la misma declaración y este código funcionó más rápido porque las declaraciones que no se almacenaron en caché eran buenas y este código rara vez se ejecutaba en la aplicación.

Ahora para arreglar las inyecciones SQL, cambié este código a,

List<PreparedStatement> pStatements = new ArrayList<>();    
for (Object object : objectList) {
    //Create a query which would be different for each object 
    PreparedStatement pStatement =connection.prepareStatement(query);
    // This query can't be added to batch because its a different query so I used list. 
    //Set parameter to pStatement using object 
    pStatements.add(pStatement);
}// Object loop
// In place of statement.executeBatch(); , I had to loop around the list & execute each update separately          
for (PreparedStatement ps : pStatements) {
    ps.executeUpdate();
}

Como puede ver, comencé a crear miles de PreparedStatementobjetos y, finalmente, no pude utilizar el procesamiento por lotes porque mi escenario exigía eso: hay miles de consultas ACTUALIZAR o INSERTAR y todas estas consultas son diferentes.

La reparación de la inyección SQL fue obligatoria sin costo de degradación del rendimiento y no creo que sea posible PreparedStatementen este escenario.

Además, cuando utiliza una instalación de procesamiento por lotes incorporada, debe preocuparse por cerrar solo una Declaración, pero con este enfoque de Lista, debe cerrar la declaración antes de volver a usarla, Reutilizar una Declaración Preparada

Sabir Khan
fuente