本页面由葡萄牙语机器翻译,可能存在错误。葡萄牙语版本为准。
9. 持久化数据
你将学到: 用 SQLite 真正地写入数据,并置于仓储之后。 前置要求: 加载数据。
完整示例(复制、粘贴即可运行)
一个 SQLite 仓储(外观)将 SQL 隔离起来,在 paint thread 之外通过 Thread 调用,
结果通过 Component.onUI 显示。bind 从 1 开始,cursor 从 0 开始,使用 try-with-resources。原样即可编译。
package com.exemplo;
import java.util.*;
import dev.crossfly.ui.*;
import dev.crossfly.ui.layout.*;
import dev.crossfly.sqlite.*;
public class MainWindow extends Window
{
private final TarefaRepositorySQLite repo = new TarefaRepositorySQLite();
private final Component listaBox = new Component();
@Override
public void onShow() throws Exception
{
super.onShow();
setLayout(new VerticalLayout());
Button add = new Button("Inserir e recarregar");
add.onAction(c -> new Thread(() -> { // I/O 在 paint thread 之外
repo.inserir("Tarefa " + System.currentTimeMillis());
var dados = repo.listar();
Component.onUI(() -> renderizar(dados)); // 返回 paint thread
}).start());
add(LAY_AUTO, add);
listaBox.setLayout(new VerticalLayout());
add(LAY_FILL, listaBox);
new Thread(() -> { // 后台初始加载
var dados = repo.listar();
Component.onUI(() -> renderizar(dados));
}).start();
}
private void renderizar(List<Tarefa> dados)
{
listaBox.removeAll();
for (Tarefa t : dados) listaBox.add(LAY_AUTO, new Label(t.id + " · " + t.titulo));
listaBox.setValidLayout(false);
Component.repaint();
}
}
class Tarefa
{
final int id; final String titulo;
Tarefa(int id, String titulo) { this.id = id; this.titulo = titulo; }
}
class TarefaRepositorySQLite
{
private static final String DB = "tarefas.db";
public TarefaRepositorySQLite()
{
try (SqliteDb db = SqliteDb.open(DB)) {
db.exec("CREATE TABLE IF NOT EXISTS tarefa(id INTEGER PRIMARY KEY, titulo TEXT, feito INTEGER)");
} catch (Exception e) { Debug.handle(e); }
}
public List<Tarefa> listar()
{
List<Tarefa> out = new ArrayList<>();
try (SqliteDb db = SqliteDb.open(DB);
SqliteCursor c = db.query("SELECT id, titulo FROM tarefa ORDER BY id")) {
while (c.next()) // getter 从 0 开始
out.add(new Tarefa(c.getInt(0), c.getString(1)));
} catch (Exception e) { Debug.handle(e); }
return out;
}
public void inserir(String titulo)
{
try (SqliteDb db = SqliteDb.open(DB)) {
db.execute("INSERT INTO tarefa(titulo, feito) VALUES(?, ?)", titulo, 0); // bind 从 1 开始
} catch (Exception e) { Debug.handle(e); }
}
}
下面的代码片段只展示核心部分(放入
onShow()内部的内容)。要运行它们,请放进上面的MainWindow骨架中。
dev.crossfly.sqlite 模块
SqliteDb/SqliteStmt/SqliteCursor —— 全都是 AutoCloseable(使用 try-with-resources),并且
在 paint thread 之外运行。
仓储(外观)
把 SQL 隔离到一个类中;UI 永远不直接说 SQL:
public class TarefaRepositorySQLite {
private static final String DB = "tarefas.db";
public TarefaRepositorySQLite() {
try (SqliteDb db = SqliteDb.open(DB)) {
db.exec("CREATE TABLE IF NOT EXISTS tarefa(id INTEGER PRIMARY KEY, titulo TEXT, feito INTEGER)");
} catch (Exception e) { Debug.handle(e); }
}
public List<Tarefa> listar() {
List<Tarefa> out = new ArrayList<>();
try (SqliteDb db = SqliteDb.open(DB);
SqliteCursor c = db.query("SELECT id, titulo FROM tarefa ORDER BY id")) {
while (c.next()) // getter 从 0 开始
out.add(new Tarefa(c.getInt(0), c.getString(1)));
} catch (Exception e) { Debug.handle(e); }
return out;
}
public void inserir(String titulo) {
try (SqliteDb db = SqliteDb.open(DB)) {
db.execute("INSERT INTO tarefa(titulo, feito) VALUES(?, ?)", titulo, 0); // bind 从 1 开始
} catch (Exception e) { Debug.handle(e); }
}
}
⚠️ 两条黄金法则
| 操作 | 起始基数 |
|---|---|
参数 bind(?) |
从 1 开始 |
| 列 getter(cursor) | 从 0 开始 |
批量写入时,使用 transaction(...)/bulkInsert(...)。对于敏感数据,用密钥打开
(SqliteDb.open(file, key),参见安全)。
本步骤构建的内容
TarefaRepositorySQLite —— 应用现在能在重启后存活了。
回顾
通过仓储使用 SQLite,try-with-resources,在 paint thread 之外;bind 从 1 开始,cursor 从 0 开始; 批量使用事务/bulk。
下一步
→ 让它响应式
另请参阅
- SQLite 持久化 · 权威来源:第 23 章 —— SQLite
这个页面有帮助吗?感谢你的反馈!