# डेटा को स्थायी बनाना

> एक रिपॉज़िटरी के पीछे SQLite (SqliteDb/SqliteCursor) — bind 1-based, getters 0-based.

Source: https://crossfly.dev/hi/docs/learn/persisting-data — CrossFly documentation. Full docs as one file: https://crossfly.dev/llms-full.txt

# 9. डेटा को स्थायी बनाना

> **आप क्या सीखेंगे:** एक रिपॉज़िटरी के पीछे, SQLite के साथ वास्तव में डेटा लिखना।
> **पूर्वापेक्षाएँ:** [डेटा लोड करना](/docs/learn/loading-data).

## पूर्ण उदाहरण (कॉपी करें, पेस्ट करें और चलाएँ)
एक SQLite रिपॉज़िटरी (फ़ेकेड) जो SQL को अलग रखता है, एक `Thread` में **paint thread के बाहर** बुलाया जाता है, और
परिणाम `Component.onUI` के माध्यम से प्रदर्शित होता है। Bind 1-based, cursor 0-based, try-with-resources. जैसा है वैसा ही कंपाइल होता है।
```java
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(() -> {               // paint thread के बाहर I/O
         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-based
            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-based
      } catch (Exception e) { Debug.handle(e); }
   }
}
```
> नीचे दिए गए स्निपेट **केवल मुख्य भाग** दिखाते हैं (जो `onShow()` के अंदर जाता है)। चलाने के लिए, उन्हें ऊपर दिए गए `MainWindow` ढाँचे में रखें।

## मॉड्यूल `dev.crossfly.sqlite`
`SqliteDb`/`SqliteStmt`/`SqliteCursor` — सभी `AutoCloseable` (try-with-resources का उपयोग करें) और
**paint thread के बाहर** चलाए जाते हैं।

## एक रिपॉज़िटरी (फ़ेकेड)
SQL को एक क्लास में अलग रखें; UI कभी सीधे SQL से बात नहीं करती:
```java
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-based
            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-based
      } catch (Exception e) { Debug.handle(e); }
   }
}
```

## ⚠️ दो सुनहरे नियम
| ऑपरेशन | बेस |
|---|---|
| पैरामीटर का **bind** (`?`) | **1-based** |
| कॉलम का **getter** (cursor) | **0-based** |

बड़े पैमाने पर लिखने के लिए, `transaction(...)`/`bulkInsert(...)` का उपयोग करें। संवेदनशील डेटा के लिए, कुंजी के साथ खोलें
(`SqliteDb.open(file, key)`, देखें [सुरक्षा](/docs/best-practices/seguranca)).

## इस चरण में आप बनाते हैं
**`TarefaRepositorySQLite`** — अब ऐप **रीस्टार्ट के बाद भी बचा रहता है**।

## संक्षेप में
रिपॉज़िटरी के माध्यम से SQLite, try-with-resources, paint thread के बाहर; **bind 1-based, cursor 0-based**;
बैच के लिए transações/bulk।

## अगले कदम
→ [रिस्पॉन्सिव बनाना](/docs/learn/making-it-responsive)

## यह भी देखें
- [SQLite के साथ स्थायित्व](/docs/data-and-services/storage-sqlite) · कैननिकल स्रोत: अध्याय 23 — SQLite
