本页面由葡萄牙语机器翻译,可能存在错误。葡萄牙语版本为准。
如何使用事务和 bulkInsert 进行批量写入
目的。 以高性能插入/更新大量行。没有事务时,每个 INSERT 都会变成一次磁盘提交(很慢)。有三个层级:(1) 用 transaction(...) 包裹所有操作,(2) 用 reset() 重用一个 SqliteStmt,(3) 使用 bulkInsert,它在原生代码中执行 bind/step/reset 循环(最快)。
使用的 API
SqliteDb.transaction(Runnable body)—— 在BEGIN/COMMIT内运行body;如果body抛出RuntimeException,则自动ROLLBACK(并重新抛出)。是保证原子性的推荐方式。SqliteDb.begin()/commit()/rollback()—— 手动控制事务(等同于exec("BEGIN"/"COMMIT"/"ROLLBACK"))。优先使用transaction(...)以免忘记回滚。SqliteDb.prepare(String sql)—— 准备一个可重用的SqliteStmt(编译一次)。你必须关闭它(try-with-resources)。SqliteStmt.bindInt/bindLong/bindDouble/bindString/bindBlob/bindNull(idx, v)和bindAll(Object...)—— bind 从 1 开始;返回this(流式)。step()执行;reset()为下一次执行做准备(保留绑定;若要清零请使用clearBindings())。SqliteDb.bulkInsert(String sql, List<Object[]> rows)—— 将sql准备一次,并在单个事务内为每个Object[]执行,采用原生循环。返回已处理的行数。每个Object[]的每个参数?对应一个元素;接受的类型:Long/Integer、Double/Float、String、byte[]、null。列类型从第一行推断 —— 请保持各行类型一致。别名:bulkUpdate、bulkDelete。
为什么要重用 statement?
prepare会编译 SQL;每一行都重复prepare会重新编译。用reset()重用只编译一次并仅重新绑定——在大循环中快得多。
完整示例(复制、粘贴并运行)
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
/** 可执行的主界面。将 launcher 指向此类(classname)并运行: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)");
// 方案 A — 事务 + 复用 statement(每次执行之间 reset)
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 开始
.bindString(2, "row" + i)
.step();
st.reset(); // 为下一行做好准备
}
}
}); // 自动 commit;抛异常则自动 rollback
// 方案 B — 原生 bulkInsert(大批量数据最快)
List<Object[]> rows = new ArrayList<>();
for (int i = 0; i < 10_000; i++)
rows.add(new Object[]{ i, "row" + i }); // 类型从第 1 行推断
int n = db.bulkInsert("INSERT INTO t(a, b) VALUES(?, ?)", rows);
System.out.println(n + " linhas inseridas");
}
}
}
下面的代码片段仅展示核心部分。要运行它,请将其放入上面的
MainWindow骨架中。
最小片段
// 已在源码验证:transaction、prepare + bind(从 1 开始)+ 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)");
// 方案 A — 事务 + 复用 statement(每次执行之间 reset)
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 开始
.bindString(2, "row" + i)
.step();
st.reset(); // 为下一行做好准备
}
}
}); // 自动 commit;抛异常则自动 rollback
// 方案 B — 原生 bulkInsert(大批量数据最快)
List<Object[]> rows = new ArrayList<>();
for (int i = 0; i < 10_000; i++)
rows.add(new Object[]{ i, "row" + i }); // 类型从第 1 行推断
int n = db.bulkInsert("INSERT INTO t(a, b) VALUES(?, ?)", rows);
System.out.println(n + " linhas inseridas");
}
深入了解
- 后台工作 · 性能与大列表
- 姊妹配方:参数化 CRUD · 在 paint 线程之外使用
这个页面有帮助吗?感谢你的反馈!