跳到内容
CrossFly 是 100% 开源在 GitHub 关注我们
CrossFlyCrossFly
开始使用

本页面由葡萄牙语机器翻译,可能存在错误。葡萄牙语版本为准。

使用 SQLite 持久化

在 CrossFly 中持久化结构化数据的规范方式是 dev.crossfly.sqlite 模块 —— 它是对原生库 cfsqlite(SQLite3MultipleCiphers)的一层薄封装。它取代了旧的 dev.crossfly.sql(JDBC)。

在绘制线程之外运行数据库,并用 Component.onUI(...) 回到 UI(见 后台工作)。SqliteDb、SqliteStmt 和 SqliteCursor 都是 AutoCloseable → 请使用 try-with-resources。

打开 / 关闭

try (SqliteDb db = SqliteDb.open("app.db")) {
   db.exec("CREATE TABLE IF NOT EXISTS cliente(id INTEGER PRIMARY KEY, nome TEXT, idade INTEGER)");
   // … 使用 db …
}   // 自动关闭

工厂方法:SqliteDb.open(String)、open(String, int flags)、open(String, byte[] key)(加密)、 openMemory()。

参数化 CRUD

// INSERT/UPDATE/DELETE → execute(sql, params...) 返回受影响的行数
db.execute("INSERT INTO cliente(nome, idade) VALUES(?, ?)", "Ana", 30);
db.execute("UPDATE cliente SET idade=? WHERE id=?", 31, 1);

// SELECT → query(sql, params...) 返回一个游标
try (SqliteCursor c = db.query("SELECT id, nome, idade FROM cliente WHERE idade > ?", 18)) {
   while (c.next()) {
      long id    = c.getLong(0);      // ⚠️ 游标的 getter 是从 0 开始的
      String nome= c.getString(1);
      int idade  = c.getInt(2);
   }
}

// 单一值:
long total = db.queryLong("SELECT COUNT(*) FROM cliente", 0);
String nome = db.queryString("SELECT nome FROM cliente WHERE id=?", 1);

⚠️ 索引的坑:绑定从 1 开始,getter 从 0 开始

操作 起始基准 示例
参数绑定(SqliteStmt) 从 1 开始 stmt.bindString(1, nome)
列getter(SqliteCursor) 从 0 开始 cursor.getString(0)

这是该模块最常见的错误 —— ? 从 1 开始,结果集的列从 0 开始。

事务与批量写入

db.transaction(() -> {                      // 自动 begin/commit(异常时 rollback)
   db.execute("INSERT INTO cliente(nome) VALUES(?)", "A");
   db.execute("INSERT INTO cliente(nome) VALUES(?)", "B");
});

// 手动的 begin/commit/rollback 也存在:
db.begin(); /* … */ db.commit();

// 批量写入(内部复用 statement):
List<Object[]> linhas = List.of(new Object[]{"A", 1}, new Object[]{"B", 2});
db.bulkInsert("INSERT INTO cliente(nome, idade) VALUES(?, ?)", linhas);

可复用的预编译 statement

try (SqliteStmt st = db.prepare("INSERT INTO cliente(nome, idade) VALUES(?, ?)")) {
   for (Cliente c : lista) {
      st.bindAll(c.nome, c.idade);   // 或 bindString(1, ...).bindInt(2, ...)
      st.step();
      st.reset();                    // 复用 statement
   }
}

可选加密

byte[] chave = obterChave();                       // 你的密钥 (byte[])
try (SqliteDb db = SqliteDb.open("secreto.db", chave)) {   // 默认使用 ChaCha20-Poly1305 加密
   // … 加密的数据库 …
   db.rekey(novaChave);                            // 更换密钥(或传入另一个以移除)
}

不提供密钥 = 普通 SQLite,零开销。支持的加密算法包括 CHACHA20(默认)、 SQLCIPHER、AES256CBC 等(Sqlite.Cipher)。

键/值表(取代旧的轻量持久化)

轻量持久化(Settings.appSettings)已被移除。对于标志/主题/上次登录,一张 键/值表即可解决:

db.exec("CREATE TABLE IF NOT EXISTS kv(k TEXT PRIMARY KEY, v TEXT)");
db.execute("INSERT OR REPLACE INTO kv(k, v) VALUES(?, ?)", "tema", "escuro");
String tema = db.queryString("SELECT v FROM kv WHERE k=?", "tema");

见文件与偏好设置。

最佳实践

  • 始终使用 try-with-resources(db、stmt、cursor)—— 避免泄漏原生句柄。
  • 批量写入 → transaction(...)/bulkInsert(...)(快数个数量级)。
  • 数据库操作放在绘制线程之外;结果用 onUI(...) 返回。

常见错误

  • 弄反起始基准:? 是从 1 开始,cursor 的列是从 0 开始。
  • 在绘制线程上运行查询,导致 UI 卡死。
  • 忘记关闭 cursor/statement(泄漏原生资源)。

另见

这个页面有帮助吗?