# SQLite के साथ पर्सिस्टेंस

> SqliteDb / SqliteStmt / SqliteCursor — CRUD, ट्रांज़ैक्शन, बल्क राइट और एन्क्रिप्शन।

Source: https://crossfly.dev/hi/docs/data-and-services/storage-sqlite — CrossFly documentation. Full docs as one file: https://crossfly.dev/llms-full.txt

# SQLite के साथ पर्सिस्टेंस

CrossFly में संरचित डेटा को पर्सिस्ट करने का **कैननिकल** तरीका `dev.crossfly.sqlite` मॉड्यूल है —
नेटिव लाइब्रेरी **cfsqlite** (SQLite3MultipleCiphers) के ऊपर एक पतली परत। यह पुराने
`dev.crossfly.sql` (JDBC) की जगह लेता है।

> **डेटाबेस को पेंट थ्रेड के बाहर चलाएँ** और `Component.onUI(...)` के साथ UI पर लौटें (देखें
> [बैकग्राउंड में काम](/docs/data-and-services/background-work))। `SqliteDb`, `SqliteStmt` और `SqliteCursor` सभी
> `AutoCloseable` हैं → **try-with-resources** का उपयोग करें।

## खोलना / बंद करना
```java
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
```java
// 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...) एक cursor देता है
try (SqliteCursor c = db.query("SELECT id, nome, idade FROM cliente WHERE idade > ?", 18)) {
   while (c.next()) {
      long id    = c.getLong(0);      // ⚠️ cursor के getter 0-based होते हैं
      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);
```

## ⚠️ इंडेक्स की पेचीदगी: bind 1-based, getters 0-based
| ऑपरेशन | बेस | उदाहरण |
|---|---|---|
| पैरामीटर का **bind** (`SqliteStmt`) | **1-based** | `stmt.bindString(1, nome)` |
| कॉलम का **getter** (`SqliteCursor`) | **0-based** | `cursor.getString(0)` |

यह मॉड्यूल की सबसे आम गलती है — `?` **1** से शुरू होते हैं, परिणाम के कॉलम **0** से।

## ट्रांज़ैक्शन और बल्क राइट
```java
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);
```

## पुनः उपयोग करने योग्य प्रिपेयर्ड स्टेटमेंट
```java
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 का पुनः उपयोग करता है
   }
}
```

## वैकल्पिक एन्क्रिप्शन
```java
byte[] chave = obterChave();                       // आपकी key (byte[])
try (SqliteDb db = SqliteDb.open("secreto.db", chave)) {   // डिफ़ॉल्ट रूप से ChaCha20-Poly1305 सिफर
   // … एन्क्रिप्टेड डेटाबेस …
   db.rekey(novaChave);                            // key बदलें (या दूसरी पास करके हटाएं)
}
```
बिना चाबी के = सामान्य SQLite, **शून्य ओवरहेड**। समर्थित सिफर में `CHACHA20` (डिफ़ॉल्ट),
`SQLCIPHER`, `AES256CBC` आदि शामिल हैं (`Sqlite.Cipher`)।

## की/वैल्यू टेबल (पुराने हल्के पर्सिस्टेंस की जगह)
हल्का पर्सिस्टेंस (`Settings.appSettings`) **हटा दिया गया है**। फ़्लैग/थीम/अंतिम लॉगिन के लिए, एक
की/वैल्यू टेबल यह हल कर देती है:
```java
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");
```
देखें [फ़ाइलें और प्राथमिकताएँ](/docs/data-and-services/files-and-preferences)।

## अच्छी प्रथाएँ
- हमेशा **try-with-resources** (db, stmt, cursor) — नेटिव हैंडल लीक होने से बचाता है।
- बैच राइट → `transaction(...)`/`bulkInsert(...)` (कई गुना तेज़)।
- डेटाबेस **पेंट थ्रेड के बाहर**; परिणाम `onUI(...)` के साथ वापस।

## आम गलतियाँ
- बेस उलट देना: `?` **1-based** है, कर्सर का कॉलम **0-based** है।
- पेंट थ्रेड पर क्वेरी चलाना और UI को फ़्रीज़ कर देना।
- कर्सर/स्टेटमेंट बंद करना भूल जाना (नेटिव संसाधन लीक होता है)।

## यह भी देखें
- [स्टेट प्रबंधन](/docs/data-and-services/state-management) · [बैकग्राउंड में काम](/docs/data-and-services/background-work)
