2012-08-23 15 views
7

A continuación se muestra el código que estoy usando para insertar multiple records(around 5000-7000) en la base de datos de Oracle mediante la declaración preparada.Uso de la declaración preparada varias veces de manera eficiente

¿La forma en que estoy trabajando actualmente es buena? ¿O puede mejorarse más usando algún batch thing?

pstatement = db_connection.prepareStatement(PDSLnPConstants.UPSERT_SQL); 

for (Entry<Integer, LinkedHashMap<Integer, String>> entry : MAPPING.entrySet()) { 

    pstatement.setInt(1, entry.getKey()); 
    pstatement.setString(2, entry.getValue().get(LnPConstants.CGUID_ID)); 
    pstatement.setString(3, entry.getValue().get(LnPConstants.PGUID_ID)); 
    pstatement.setString(4, entry.getValue().get(LnPConstants.SGUID_ID)); 
    pstatement.setString(5, entry.getValue().get(LnPConstants.UID_ID)); 
    pstatement.setString(6, entry.getValue().get(LnPConstants.ULOC_ID)); 
    pstatement.setString(7, entry.getValue().get(LnPConstants.SLOC_ID)); 
    pstatement.setString(8, entry.getValue().get(LnPConstants.PLOC_ID)); 
    pstatement.setString(9, entry.getValue().get(LnPConstants.ALOC_ID)); 
    pstatement.setString(10, entry.getValue().get(LnPConstants.SITE_ID)); 
    pstatement.executeUpdate(); 

    pstatement.clearParameters(); 
} 

Código Udpated que estoy utilizando: -

public void runNextCommand() { 

    Connection db_connection = null; 
    PreparedStatement pstatement = null; 
    int batchLimit = 1000; 
    boolean autoCommit = false; 

    try { 
     db_connection = getDBConnection(); 

     autoCommit = db_connection.getAutoCommit(); 
     db_connection.setAutoCommit(false); //Turn off autoCommit 
     pstatement = db_connection.prepareStatement(LnPConstants.UPSERT_SQL); // create a statement 

     for (Entry<Integer, LinkedHashMap<Integer, String>> entry : GUID_ID_MAPPING.entrySet()) { 
      pstatement.setInt(1, entry.getKey()); 
      pstatement.setString(2, entry.getValue().get(LnPConstants.CGUID_ID)); 
      pstatement.setString(3, entry.getValue().get(LnPConstants.PGUID_ID)); 
      pstatement.setString(4, entry.getValue().get(LnPConstants.SGUID_ID)); 
      pstatement.setString(5, entry.getValue().get(LnPConstants.UID_ID)); 
      pstatement.setString(6, entry.getValue().get(LnPConstants.ULOC_ID)); 
      pstatement.setString(7, entry.getValue().get(LnPConstants.SLOC_ID)); 
      pstatement.setString(8, entry.getValue().get(LnPConstants.PLOC_ID)); 
      pstatement.setString(9, entry.getValue().get(LnPConstants.ALOC_ID)); 
      pstatement.setString(10, entry.getValue().get(LnPConstants.SITE_ID)); 
      pstatement.addBatch(); 

      batchLimit--; 

      if(batchLimit == 0){ 
       pstatement.executeBatch(); 
       pstatement.clearBatch(); 
       batchLimit = 1000; 
      } 
      pstatement.clearParameters(); 
     } 

    } catch (SQLException e) { 
     getLogger().log(LogLevel.ERROR, e); 
    } finally { 
     try { 
      pstatement.executeBatch(); 
      db_connection.commit(); 
      db_connection.setAutoCommit(autoCommit); 
     } catch (SQLException e1) { 
      getLogger().log(LogLevel.ERROR, e1.getMessage(), e1.fillInStackTrace()); 
     } 

     if (pstatement != null) { 
      try { 
       pstatement.close(); 
       pstatement = null; 
      } catch (SQLException e) { 
       getLogger().log(LogLevel.ERROR, e.getMessage(), e.fillInStackTrace()); 
      } 
     } 
     if (db_connection!= null) { 
      try { 
       db_connection.close(); 
       db_connection = null; 
      } catch (SQLException e) { 
       getLogger().log(LogLevel.ERROR, e.getMessage(), e.fillInStackTrace()); 
      } 
     } 
    } 
} 
+0

(Asegúrese de que las transacciones se estén utilizando correctamente. De lo contrario, el código publicado "no será bueno", incluso [reutilizando] las declaraciones preparadas. Es decir, INSERT/UPDATE es "rápido". COMMIT es "lento"). –

Respuesta

8

Se puede pensar en el uso de addBatch() y la ejecución de una parte posterior de las declaraciones de una sola vez. Además, como comentó @pst en su pregunta, considere usar .

La forma en que lo haría es el siguiente:

boolean autoCommit = connection.getAutoCommit(); 
try{ 
    connection.setAutoCommit(false //Turn off autoCommit 
    pstatement = db_connection.prepareStatement(PDSLnPConstants.UPSERT_SQL); 

    int batchLimit = 1000; 

    try{ 
     for (Entry<Integer, LinkedHashMap<Integer, String>> entry : MAPPING.entrySet()) { 
      pstatement.setInt(1, entry.getKey()); 
      pstatement.setString(2, entry.getValue().get(LnPConstants.CGUID_ID)); 
      pstatement.setString(3, entry.getValue().get(LnPConstants.PGUID_ID)); 
      pstatement.setString(4, entry.getValue().get(LnPConstants.SGUID_ID)); 
      pstatement.setString(5, entry.getValue().get(LnPConstants.UID_ID)); 
      pstatement.setString(6, entry.getValue().get(LnPConstants.ULOC_ID)); 
      pstatement.setString(7, entry.getValue().get(LnPConstants.SLOC_ID)); 
      pstatement.setString(8, entry.getValue().get(LnPConstants.PLOC_ID)); 
      pstatement.setString(9, entry.getValue().get(LnPConstants.ALOC_ID)); 
      pstatement.setString(10, entry.getValue().get(LnPConstants.SITE_ID)); 

      pstatement.addBatch(); 
      batchLimit--; 

      if(batchLimit == 0){ 
       pstatement.executeBatch(); 
       pstatement.clearBatch 
       batchLimit = 1000; 
      } 
      pstatement.clearParameters(); 
     } 
    }finally{ 
     //for the remaining ones 
     pstatement.executeBatch(); 

     //commit your updates 
     connection.commit(); 
    } 
}finally{ 
    connection.setAutoCommit(autoCommit); 
} 

La idea es establecer un límite para actualizaciones por lotes y ejecutar una actualización de base de datos sólo cuando se alcanza un límite determinado. De esta manera estás limitando una llamada a la base de datos a una vez cada batchLimit que hayas definido. De esta forma sería más rápido.

También tenga en cuenta para el transaction, que acabo de mostrar cómo y cuándo commit. Puede que este no sea siempre el punto correcto para commit porque esta decisión se basaría en su requisito. Es posible que también desee realizar un rollback en caso de una excepción. Entonces depende de ti decidir.

Eche un vistazo a "Using Transaction" tutorial para obtener una mejor imagen de cómo usar transaction.

+0

¿Puede proporcionarme una base de ejemplo en mi código para que tenga una idea clara de cómo usar 'addBatch()' aquí? Será de gran ayuda. – AKIWEB

+0

@ Nevzz03: actualicé mi respuesta con un código de muestra. Espero que esto ayude :) – Sujay

+0

Gracias a Sujay por la respuesta detallada.Apreciado tu ayuda. Acabo de actualizar mi pregunta con el código completo que estoy usando actualmente como lo sugirió después de realizar cambios. ¿Puedes echar un vistazo y decirme si todo te queda bien y no estoy haciendo nada mal? – AKIWEB

1

Su código me parece bueno.

Solo para la limpieza del código, pondría entry.getValue() en una variable (llámelo value).
Y no hay necesidad de llamar al clearParameters().

Por último, recuerde desechar correctamente la declaración preparada cuando ya no la necesite (close()).

+0

Gracias gd1 por las sugerencias. ¿Por qué crees que no es necesario llamar a 'clearParameters()'? Solo quería saber desde el punto de vista del conocimiento. – AKIWEB

+1

Cuando establece un parámetro, el valor anterior se borra. :) – gd1

1

Sí, hacer actualizaciones por lotes mejoraría significativamente su rendimiento. Solo busque google, mi respuesta preferida es this one from Mkyong.com. De lo contrario, tu código se ve bien. "clearParameters()" no es realmente necesario, incluso podría consumir algunos ciclos de procesador. Importante: si se habilitó AutoCommit, no olvide deshabilitarlo antes, y habilitarlo después de hacer las actualizaciones, esto trae de nuevo una tremenda mejora.

PS

Por encima de recomendación se basa también en mi experiencia. Acabo de señalar que esta pregunta ya fue hecha here at Stackoverflow y la respuesta es muy detallada. Puede encontrar más información sobre los estados preparados y los lotes en Oracle docs here y sobre Transactions (AutoCommit) here.

Cuestiones relacionadas