This page was machine-translated from Portuguese and may contain errors. The Portuguese version is canonical.
How to write in bulk with a transaction and bulkInsert
Purpose. Insert/update many rows with high performance. Without a transaction, each INSERT becomes a commit to disk (slow). There are three levels: (1) wrap everything in transaction(...), (2) reuse a SqliteStmt with reset(), (3) use bulkInsert, which does the bind/step/reset loop in native code (fastest).
APIs used
SqliteDb.transaction(Runnable body)— runsbodyinsideBEGIN/COMMIT; performs an automaticROLLBACKifbodythrows aRuntimeException(and rethrows). The recommended form for atomicity.SqliteDb.begin()/commit()/rollback()— manual transaction control (equivalent toexec("BEGIN"/"COMMIT"/"ROLLBACK")). Prefertransaction(...)so you don’t forget the rollback.SqliteDb.prepare(String sql)— prepares a reusableSqliteStmt(compiles once). You must close it (try-with-resources).SqliteStmt.bindInt/bindLong/bindDouble/bindString/bindBlob/bindNull(idx, v)andbindAll(Object...)— 1-based bind; returnthis(fluent).step()executes;reset()prepares for the next execution (keeps the binds; useclearBindings()if you want to reset them).SqliteDb.bulkInsert(String sql, List<Object[]> rows)— preparessqlonce and executes it for eachObject[]within a single transaction, in a native loop. Returns the number of rows processed. EachObject[]has one element per?parameter; accepted types:Long/Integer,Double/Float,String,byte[],null. The column types are inferred from the FIRST row — keep the rows homogeneous. Aliases:bulkUpdate,bulkDelete.
Why reuse the statement?
preparecompiles the SQL; repeatingpreparefor each row recompiles it. Reusing it withreset()compiles once and only re-binds — much faster in large loops.
Full example (copy, paste and run)
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
/** Runnable main screen. Point the launcher (classname) and run: 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)");
// Option A — transaction + reused statement (reset between executions)
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(); // ready for the next row
}
}
}); // automatic commit; automatic rollback if it throws
// Option B — native bulkInsert (fastest for large volumes)
List<Object[]> rows = new ArrayList<>();
for (int i = 0; i < 10_000; i++)
rows.add(new Object[]{ i, "row" + i }); // types inferred from the 1st row
int n = db.bulkInsert("INSERT INTO t(a, b) VALUES(?, ?)", rows);
System.out.println(n + " linhas inseridas");
}
}
}
The snippet below shows only the core. To run it, place it inside the
MainWindowskeleton above.
Minimal snippet
// verified at source: 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)");
// Option A — transaction + reused statement (reset between executions)
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(); // ready for the next row
}
}
}); // automatic commit; automatic rollback if it throws
// Option B — native bulkInsert (fastest for large volumes)
List<Object[]> rows = new ArrayList<>();
for (int i = 0; i < 10_000; i++)
rows.add(new Object[]{ i, "row" + i }); // types inferred from the 1st row
int n = db.bulkInsert("INSERT INTO t(a, b) VALUES(?, ?)", rows);
System.out.println(n + " linhas inseridas");
}
Dig deeper
- Background work · Performance and large lists
- Sibling recipes: Parameterized CRUD · Use outside the paint thread
Was this page helpful?Thanks for the feedback!