2010-03-18 23 views
82

en el caso de usar PreparedStatement con una única conexión común sin ningún grupo, ¿puedo recrear una instancia para cada operación dml/sql manteniendo el poder de las declaraciones preparadas?Reutilizando un Estado Preparado varias veces

quiero decir:

for (int i=0; i<1000; i++) { 
    PreparedStatement preparedStatement = connection.prepareStatement(sql); 
    preparedStatement.setObject(1, someValue); 
    preparedStatement.executeQuery(); 
    preparedStatement.close(); 
} 

en lugar de:

PreparedStatement preparedStatement = connection.prepareStatement(sql); 
for (int i=0; i<1000; i++) { 
    preparedStatement.clearParameters(); 
    preparedStatement.setObject(1, someValue); 
    preparedStatement.executeQuery(); 
} 
preparedStatement.close(); 

mi pregunta surge por el hecho de que yo quiero poner este código en un entorno multiproceso, me puede dar un consejo? gracias

Respuesta

126

La segunda forma es un poco más eficiente, pero de una manera mucho mejor es para su ejecución en lotes:

public void executeBatch(List<Entity> entities) throws SQLException { 
    try (
     Connection connection = dataSource.getConnection(); 
     PreparedStatement statement = connection.prepareStatement(SQL); 
    ) { 
     for (Entity entity : entities) { 
      statement.setObject(1, entity.getSomeProperty()); 
      // ... 

      statement.addBatch(); 
     } 

     statement.executeBatch(); 
    } 
} 

Usted es sin embargo dependiente de la implementación del controlador JDBC cuántos lotes se podía ejecutar a la vez . Por ejemplo, puede que desee ejecutarlos cada 1000 lotes:

public void executeBatch(List<Entity> entities) throws SQLException { 
    try (
     Connection connection = dataSource.getConnection(); 
     PreparedStatement statement = connection.prepareStatement(SQL); 
    ) { 
     int i = 0; 

     for (Entity entity : entities) { 
      statement.setObject(1, entity.getSomeProperty()); 
      // ... 

      statement.addBatch(); 
      i++; 

      if (i % 1000 == 0 || i == entities.size()) { 
       statement.executeBatch(); // Execute every 1000 items. 
      } 
     } 
    } 
} 

En cuanto a los entornos multiproceso, que no es necesario preocuparse por esto si usted adquiere y cerrar la conexión y el estado en el menor grado posible dentro del mismo bloque de método según la expresión JDBC normal usando la instrucción try-with-resources como se muestra en los fragmentos de arriba.

Si esos lotes son transaccionales, entonces desea desactivar la confirmación automática de la conexión y solo confirmar la transacción cuando finalicen todos los lotes. De lo contrario, puede dar lugar a una base de datos sucia cuando el primer grupo de lotes tuvo éxito y el otro no.

public void executeBatch(List<Entity> entities) throws SQLException { 
    try (Connection connection = dataSource.getConnection()) { 
     connection.setAutoCommit(false); 

     try (PreparedStatement statement = connection.prepareStatement(SQL)) { 
      // ... 

      try { 
       connection.commit(); 
      } catch (SQLException e) { 
       connection.rollback(); 
       throw e; 
      } 
     } 
    } 
} 
+0

'dentro del mismo bloque de método' - quiere decir que cada subproceso tendrá su propia pila y estas conexiones y declaraciones están en pila desde un lado y de otra fuente de datos dará a cada nueva llamada de executeFunction (== cada hilo) instancia de conexión separada. ¿Te entiendo, verdad?" –

+0

Estoy haciendo el primer método, pero mientras controlo en SQL Profiler, veo repetidas declaraciones preparadas múltiples en lugar de una. No pude entender por qué se muestran varias declaraciones. Se requiere asistencia. – Max

11

El bucle en su código es solo un ejemplo simplificado, ¿verdad?

Sería mejor crear el Estado Preparado una sola vez, y volver a utilizarlo una y otra vez en el ciclo.

En situaciones en las que eso no es posible (porque complicaba demasiado el flujo del programa), sigue siendo beneficioso usar un PreparedStatement, incluso si lo usa solo una vez, porque el servidor trabaja (analizando el SQL y almacenamiento en caché del plan de ejecución), aún se reducirá.

Para resolver la situación en la que desea volver a utilizar el PreparedStatement del lado de Java, algunos controladores JDBC (como Oracle) tienen una función de almacenamiento en caché: si crea un PreparedStatement para el mismo SQL en la misma conexión, lo hará darle la misma instancia (en caché).

Acerca de multi-threading: no creo que las conexiones JDBC puedan compartirse entre varios hilos (es decir, usadas al mismo tiempo por varios hilos) de todos modos. Cada hilo debe obtener su propia conexión del grupo, usarla piscina de nuevo.

+1

De hecho, la conexión tiene su hilo exclusivo y cada instrucción se ejecuta en ella, pero accedo a través de una pila expuesta de declaraciones preparadas a esa cadena. Entonces, otros subprocesos concurrentes inicialmente solo pasan los parámetros necesarios para compilar todas las declaraciones preparadas, pero luego pueden modificar los parámetros simultáneamente –

Cuestiones relacionadas