Saltar al contenido
CrossFly es 100% open sourceSíguenos en GitHub
CrossFlyCrossFly
Empezar

Esta página fue traducida automáticamente del portugués y puede contener errores. La versión en portugués es la canónica.

Cómo escribir en masa con transacción y bulkInsert

Propósito. Insertar/actualizar muchas filas con alto rendimiento. Sin transacción, cada INSERT se convierte en un commit en disco (lento). Hay tres niveles: (1) envolver todo en transaction(...), (2) reutilizar un SqliteStmt con reset(), (3) usar bulkInsert, que hace el loop bind/step/reset en código nativo (más rápido).

APIs usadas

  • SqliteDb.transaction(Runnable body) — ejecuta body dentro de BEGIN/COMMIT; hace ROLLBACK automático si body lanza RuntimeException (y la relanza). Forma recomendada para atomicidad.
  • SqliteDb.begin() / commit() / rollback() — control manual de la transacción (equivalen a exec("BEGIN"/"COMMIT"/"ROLLBACK")). Prefiere transaction(...) para no olvidar el rollback.
  • SqliteDb.prepare(String sql) — prepara un SqliteStmt reutilizable (compila una vez). Debes cerrarlo (try-with-resources).
  • SqliteStmt.bindInt/bindLong/bindDouble/bindString/bindBlob/bindNull(idx, v) y bindAll(Object...) — bind 1-based; retornan this (fluido). step() ejecuta; reset() prepara para la próxima ejecución (mantiene los binds; usa clearBindings() si quieres reiniciarlos).
  • SqliteDb.bulkInsert(String sql, List<Object[]> rows) — prepara sql una vez y lo ejecuta para cada Object[] dentro de una única transacción, en loop nativo. Retorna el nº de filas procesadas. Cada Object[] tiene un elemento por parámetro ?; tipos aceptados: Long/Integer, Double/Float, String, byte[], null. Los tipos de columna se infieren de la PRIMERA fila — mantén las filas homogéneas. Alias: bulkUpdate, bulkDelete.

¿Por qué reutilizar el statement? prepare compila el SQL; repetir prepare en cada fila recompila. Reutilizar con reset() compila una vez y solo re-vincula — mucho más rápido en loops grandes.

Ejemplo completo (copiar, pegar y ejecutar)

package com.exemplo;

import java.util.*;                          // List, ArrayList

import dev.crossfly.ui.*;                     // Window
import dev.crossfly.ui.layout.*;             // VerticalLayout
import dev.crossfly.sqlite.*;                // SqliteDb, SqliteStmt

/** Pantalla principal ejecutable. Apunta el launcher (classname) y corre: gradlew :lwjgl3:run */
public class MainWindow extends Window
{
   @Override
   public void onShow() throws Exception
   {
      super.onShow();
      setLayout(new VerticalLayout());

      try (SqliteDb db = SqliteDb.open("app.db"))
      {
         db.exec("CREATE TABLE IF NOT EXISTS t(a INTEGER, b TEXT)");

         // Opción A — transacción + statement reutilizado (reset entre ejecuciones)
         db.transaction(() ->
         {
            try (SqliteStmt st = db.prepare("INSERT INTO t(a, b) VALUES(?, ?)"))
            {
               for (int i = 0; i < 10_000; i++)
               {
                  st.bindInt(1, i)            // idx 1-based
                    .bindString(2, "row" + i)
                    .step();
                  st.reset();                 // listo para la siguiente fila
               }
            }
         }); // commit automático; rollback automático si lanza

         // Opción B — bulkInsert nativo (el más rápido para grandes volúmenes)
         List<Object[]> rows = new ArrayList<>();
         for (int i = 0; i < 10_000; i++)
            rows.add(new Object[]{ i, "row" + i });   // tipos inferidos de la 1ª fila
         int n = db.bulkInsert("INSERT INTO t(a, b) VALUES(?, ?)", rows);
         System.out.println(n + " linhas inseridas");
      }
   }
}

El fragmento de abajo muestra solo el núcleo. Para ejecutarlo, colócalo en el esqueleto MainWindow de arriba.

Fragmento mínimo

// verificado en la fuente: transaction, prepare + bind (1-based) + step + reset, bulkInsert
import dev.crossfly.sqlite.*;
import java.util.*;

try (SqliteDb db = SqliteDb.open("app.db"))
{
   db.exec("CREATE TABLE IF NOT EXISTS t(a INTEGER, b TEXT)");

   // Opción A — transacción + statement reutilizado (reset entre ejecuciones)
   db.transaction(() ->
   {
      try (SqliteStmt st = db.prepare("INSERT INTO t(a, b) VALUES(?, ?)"))
      {
         for (int i = 0; i < 10_000; i++)
         {
            st.bindInt(1, i)            // idx 1-based
              .bindString(2, "row" + i)
              .step();
            st.reset();                 // listo para la siguiente fila
         }
      }
   }); // commit automático; rollback automático si lanza

   // Opción B — bulkInsert nativo (el más rápido para grandes volúmenes)
   List<Object[]> rows = new ArrayList<>();
   for (int i = 0; i < 10_000; i++)
      rows.add(new Object[]{ i, "row" + i });   // tipos inferidos de la 1ª fila
   int n = db.bulkInsert("INSERT INTO t(a, b) VALUES(?, ?)", rows);
   System.out.println(n + " linhas inseridas");
}

Profundiza

¿Te resultó útil esta página?