Esta página fue traducida automáticamente del portugués y puede contener errores. La versión en portugués es la canónica.
Persistencia con SQLite
La forma canónica de persistir datos estructurados en CrossFly es el módulo dev.crossfly.sqlite —
una capa fina sobre la lib nativa cfsqlite (SQLite3MultipleCiphers). Sustituye al antiguo
dev.crossfly.sql (JDBC).
Ejecuta la base de datos fuera del paint thread y vuelve a la UI con
Component.onUI(...)(ver Trabajo en segundo plano).SqliteDb,SqliteStmtySqliteCursorson todosAutoCloseable→ usa try-with-resources.
Abrir / cerrar
try (SqliteDb db = SqliteDb.open("app.db")) {
db.exec("CREATE TABLE IF NOT EXISTS cliente(id INTEGER PRIMARY KEY, nome TEXT, idade INTEGER)");
// … usar db …
} // se cierra por sí solo
Fábricas: SqliteDb.open(String), open(String, int flags), open(String, byte[] key) (cifrado),
openMemory().
CRUD parametrizado
// INSERT/UPDATE/DELETE → execute(sql, params...) retorna nº de filas afectadas
db.execute("INSERT INTO cliente(nome, idade) VALUES(?, ?)", "Ana", 30);
db.execute("UPDATE cliente SET idade=? WHERE id=?", 31, 1);
// SELECT → query(sql, params...) devuelve un cursor
try (SqliteCursor c = db.query("SELECT id, nome, idade FROM cliente WHERE idade > ?", 18)) {
while (c.next()) {
long id = c.getLong(0); // ⚠️ los getters del cursor son 0-based
String nome= c.getString(1);
int idade = c.getInt(2);
}
}
// valor único:
long total = db.queryLong("SELECT COUNT(*) FROM cliente", 0);
String nome = db.queryString("SELECT nome FROM cliente WHERE id=?", 1);
⚠️ El truco de los índices: bind 1-based, getters 0-based
| Operación | Base | Ejemplo |
|---|---|---|
bind de parámetro (SqliteStmt) |
1-based | stmt.bindString(1, nome) |
getter de columna (SqliteCursor) |
0-based | cursor.getString(0) |
Es el error más común del módulo — los ? empiezan en 1, las columnas del resultado en 0.
Transacciones y escritura masiva
db.transaction(() -> { // begin/commit automático (rollback en excepción)
db.execute("INSERT INTO cliente(nome) VALUES(?)", "A");
db.execute("INSERT INTO cliente(nome) VALUES(?)", "B");
});
// begin/commit/rollback manuales también existen:
db.begin(); /* … */ db.commit();
// escritura en masa (statement reutilizado internamente):
List<Object[]> linhas = List.of(new Object[]{"A", 1}, new Object[]{"B", 2});
db.bulkInsert("INSERT INTO cliente(nome, idade) VALUES(?, ?)", linhas);
Statement preparado reutilizable
try (SqliteStmt st = db.prepare("INSERT INTO cliente(nome, idade) VALUES(?, ?)")) {
for (Cliente c : lista) {
st.bindAll(c.nome, c.idade); // o bindString(1, ...).bindInt(2, ...)
st.step();
st.reset(); // reutiliza el statement
}
}
Cifrado opcional
byte[] chave = obterChave(); // tu clave (byte[])
try (SqliteDb db = SqliteDb.open("secreto.db", chave)) { // cifra ChaCha20-Poly1305 por defecto
// … base de datos cifrada …
db.rekey(novaChave); // cambia la clave (o la elimina pasando otra)
}
Sin clave = SQLite normal, cero overhead. Las cifras soportadas incluyen CHACHA20 (por defecto),
SQLCIPHER, AES256CBC, etc. (Sqlite.Cipher).
Tabla clave/valor (sustituye la antigua persistencia ligera)
La persistencia ligera (Settings.appSettings) fue eliminada. Para flags/tema/último login, una
tabla clave/valor lo resuelve:
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");
Buenas prácticas
- Usa siempre try-with-resources (db, stmt, cursor) — evita filtrar handles nativos.
- Escritura por lotes →
transaction(...)/bulkInsert(...)(órdenes de magnitud más rápido). - Base de datos fuera del paint thread; resultados de vuelta con
onUI(...).
Errores comunes
- Cambiar las bases:
?es 1-based, la columna del cursor es 0-based. - Ejecutar consultas en el paint thread y bloquear la UI.
- Olvidar cerrar cursor/statement (filtra recurso nativo).