Daten mit SQLite speichern

Das Speichern von Daten in einer Datenbank ist ideal für sich wiederholende oder strukturierte Daten, wie Kontaktinformationen. Auf dieser Seite wird davon ausgegangen, dass Sie SQL-Datenbanken im Allgemeinen SQLite-Datenbanken unter Android Die APIs, die Sie zum Verwenden einer Datenbank benötigen für Android sind im Paket android.database.sqlite verfügbar.

Achtung:Diese APIs sind zwar leistungsstark, entsprechen aber relativ wenig und erfordern viel Zeit und Aufwand:

  • Es gibt keine Prüfung der Kompilierungszeit von SQL-Rohabfragen. Da Ihre Daten Diagrammänderungen müssen Sie die betroffenen SQL-Abfragen manuell aktualisieren. Dieses kann zeitaufwendig und fehleranfällig sein.
  • Sie müssen viel Boilerplate-Code verwenden, um zwischen SQL-Abfragen zu konvertieren. und Datenobjekte.

Aus diesen Gründen empfehlen wir dringend die Verwendung des Raumpersistenzbibliothek als Abstraktionsebene für den Zugriff auf Informationen in der SQLite Ihrer App Datenbanken.

Schema definieren und Vertrag

Eines der Hauptprinzipien von SQL-Datenbanken ist das Schema: ein Deklaration der Datenbankstruktur. Das Schema wird in der SQL- Anweisungen, mit denen Sie Ihre Datenbank erstellen. Vielleicht finden Sie es hilfreich, eine Companion-Klasse erstellen, auch als Contract-Klasse bezeichnet, die explizit angibt, das Layout Ihres Schemas systematisch und selbstdokumentierend darzustellen.

Eine Vertragsklasse ist ein Container für Konstanten, die Namen für URIs definieren, Tabellen und Spalten. Mit der Kontraktionsklasse können Sie dieselben Konstanten für alle anderen Klassen im selben Paket. So können Sie eine Spalte und im gesamten Code verwenden.

Eine gute Methode zur Organisation einer Vertragsklasse besteht darin, für die gesamte Datenbank auf der Stammebene der Klasse. Erstellen Sie dann eine innere Klasse für jede Tabelle. Jede innere Klasse listet die Spalten der entsprechenden Tabelle auf.

Hinweis: Wenn Sie BaseColumns implementieren, kann Ihre innere Klasse eine primäre Schlüsselfeld namens _ID, das von einigen Android-Klassen wie CursorAdapter erwartet wird. Sie ist nicht erforderlich, kann aber Ihrer Datenbank helfen. mit dem Android-Framework harmonieren.

Im folgenden Vertrag werden beispielsweise die Tabellennamen und Spaltennamen für eine einzelne Tabelle, die einen RSS-Feed darstellt:

Kotlin

object FeedReaderContract {
    // Table contents are grouped together in an anonymous object.
    object FeedEntry : BaseColumns {
        const val TABLE_NAME = "entry"
        const val COLUMN_NAME_TITLE = "title"
        const val COLUMN_NAME_SUBTITLE = "subtitle"
    }
}

Java

public final class FeedReaderContract {
    // To prevent someone from accidentally instantiating the contract class,
    // make the constructor private.
    private FeedReaderContract() {}

    /* Inner class that defines the table contents */
    public static class FeedEntry implements BaseColumns {
        public static final String TABLE_NAME = "entry";
        public static final String COLUMN_NAME_TITLE = "title";
        public static final String COLUMN_NAME_SUBTITLE = "subtitle";
    }
}

Datenbank mit einem SQL-Hilfsprogramm erstellen

Nachdem Sie das Aussehen Ihrer Datenbank definiert haben, sollten Sie Methoden implementieren. die die Datenbank und die Tabellen erstellen und verwalten. Hier sind einige typische Anweisungen zum Erstellen und Löschen einer Tabelle:

Kotlin

private const val SQL_CREATE_ENTRIES =
        "CREATE TABLE ${FeedEntry.TABLE_NAME} (" +
                "${BaseColumns._ID} INTEGER PRIMARY KEY," +
                "${FeedEntry.COLUMN_NAME_TITLE} TEXT," +
                "${FeedEntry.COLUMN_NAME_SUBTITLE} TEXT)"

private const val SQL_DELETE_ENTRIES = "DROP TABLE IF EXISTS ${FeedEntry.TABLE_NAME}"

Java

private static final String SQL_CREATE_ENTRIES =
    "CREATE TABLE " + FeedEntry.TABLE_NAME + " (" +
    FeedEntry._ID + " INTEGER PRIMARY KEY," +
    FeedEntry.COLUMN_NAME_TITLE + " TEXT," +
    FeedEntry.COLUMN_NAME_SUBTITLE + " TEXT)";

private static final String SQL_DELETE_ENTRIES =
    "DROP TABLE IF EXISTS " + FeedEntry.TABLE_NAME;

Genau wie Dateien, die Sie auf dem internen Speicher Ihres Geräts Speicher speichern, speichert Android die Datenbank im privaten Ordner Ihrer App. Ihre Daten da sich dieser Bereich standardmäßig für andere Apps oder den Nutzer zugänglich sind.

Die Klasse SQLiteOpenHelper enthält eine nützliche eine Reihe von APIs zum Verwalten Ihrer Datenbank. Wenn Sie diese Klasse verwenden, um Verweise auf Ihre Datenbank zu erhalten, führt die potenziell lang andauernde Vorgänge zum Erstellen und Aktualisieren der Datenbank und nicht beim Start der App. Sie müssen lediglich getWritableDatabase() oder getReadableDatabase().

Hinweis:Da sie lange andauern können, sollten Sie getWritableDatabase() oder getReadableDatabase() in einem Hintergrundthread aufrufen. Weitere Informationen finden Sie unter Threading unter Android.

Erstellen Sie zur Verwendung von SQLiteOpenHelper eine abgeleitete Klasse, die überschreibt onCreate() und onUpgrade(). Sie können auch die Funktion onDowngrade() oder onOpen()-Methoden, aber sie sind nicht erforderlich.

Hier sehen Sie z. B. eine Implementierung von SQLiteOpenHelper, die verwendet einige der oben aufgeführten Befehle:

Kotlin

class FeedReaderDbHelper(context: Context) : SQLiteOpenHelper(context, DATABASE_NAME, null, DATABASE_VERSION) {
    override fun onCreate(db: SQLiteDatabase) {
        db.execSQL(SQL_CREATE_ENTRIES)
    }
    override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
        // This database is only a cache for online data, so its upgrade policy is
        // to simply to discard the data and start over
        db.execSQL(SQL_DELETE_ENTRIES)
        onCreate(db)
    }
    override fun onDowngrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
        onUpgrade(db, oldVersion, newVersion)
    }
    companion object {
        // If you change the database schema, you must increment the database version.
        const val DATABASE_VERSION = 1
        const val DATABASE_NAME = "FeedReader.db"
    }
}

Java

public class FeedReaderDbHelper extends SQLiteOpenHelper {
    // If you change the database schema, you must increment the database version.
    public static final int DATABASE_VERSION = 1;
    public static final String DATABASE_NAME = "FeedReader.db";

    public FeedReaderDbHelper(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(SQL_CREATE_ENTRIES);
    }
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        // This database is only a cache for online data, so its upgrade policy is
        // to simply to discard the data and start over
        db.execSQL(SQL_DELETE_ENTRIES);
        onCreate(db);
    }
    public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        onUpgrade(db, oldVersion, newVersion);
    }
}

Instanziieren Sie die abgeleitete Klasse von, um auf Ihre Datenbank zuzugreifen SQLiteOpenHelper:

Kotlin

val dbHelper = FeedReaderDbHelper(context)

Java

FeedReaderDbHelper dbHelper = new FeedReaderDbHelper(getContext());

Informationen in einer Datenbank speichern

Fügen Sie Daten in die Datenbank ein, indem Sie ein ContentValues-Objekt übergeben. an die Methode insert() übergeben:

Kotlin

// Gets the data repository in write mode
val db = dbHelper.writableDatabase

// Create a new map of values, where column names are the keys
val values = ContentValues().apply {
    put(FeedEntry.COLUMN_NAME_TITLE, title)
    put(FeedEntry.COLUMN_NAME_SUBTITLE, subtitle)
}

// Insert the new row, returning the primary key value of the new row
val newRowId = db?.insert(FeedEntry.TABLE_NAME, null, values)

Java

// Gets the data repository in write mode
SQLiteDatabase db = dbHelper.getWritableDatabase();

// Create a new map of values, where column names are the keys
ContentValues values = new ContentValues();
values.put(FeedEntry.COLUMN_NAME_TITLE, title);
values.put(FeedEntry.COLUMN_NAME_SUBTITLE, subtitle);

// Insert the new row, returning the primary key value of the new row
long newRowId = db.insert(FeedEntry.TABLE_NAME, null, values);

Das erste Argument für insert() ist einfach der Tabellenname.

Das zweite Argument teilt dem Framework mit, was zu tun ist, wenn das Framework ContentValues ist leer. Sie haben also put beliebige Werte). Wenn Sie den Namen einer Spalte angeben, fügt das Framework eine Zeile ein und legt die dieser Spalte auf null setzen. Wenn Sie null angeben, wie in diesem Codebeispiel enthält, fügt das Framework keine Zeile ein, wenn keine Werte vorhanden sind.

Die Methode insert() gibt die ID für die neu erstellte Zeile. Andernfalls wird -1 zurückgegeben, wenn beim Einfügen der Daten ein Fehler aufgetreten ist. Das kann passieren, wenn es einen Konflikt mit bereits vorhandenen Daten in der Datenbank gibt.

Informationen aus einer Datenbank lesen

Verwenden Sie zum Lesen aus einer Datenbank die Methode query() und übergeben Sie Ihre Auswahlkriterien und die gewünschten Spalten. Die Methode kombiniert Elemente von insert(). und update(), mit Ausnahme der Spaltenliste definiert die Daten, die Sie abrufen möchten (die "Projektion"), nicht die einzufügenden Daten. Die Ergebnisse der Abfrage werden in einem Cursor-Objekt zurückgegeben.

Kotlin

val db = dbHelper.readableDatabase

// Define a projection that specifies which columns from the database
// you will actually use after this query.
val projection = arrayOf(BaseColumns._ID, FeedEntry.COLUMN_NAME_TITLE, FeedEntry.COLUMN_NAME_SUBTITLE)

// Filter results WHERE "title" = 'My Title'
val selection = "${FeedEntry.COLUMN_NAME_TITLE} = ?"
val selectionArgs = arrayOf("My Title")

// How you want the results sorted in the resulting Cursor
val sortOrder = "${FeedEntry.COLUMN_NAME_SUBTITLE} DESC"

val cursor = db.query(
        FeedEntry.TABLE_NAME,   // The table to query
        projection,             // The array of columns to return (pass null to get all)
        selection,              // The columns for the WHERE clause
        selectionArgs,          // The values for the WHERE clause
        null,                   // don't group the rows
        null,                   // don't filter by row groups
        sortOrder               // The sort order
)

Java

SQLiteDatabase db = dbHelper.getReadableDatabase();

// Define a projection that specifies which columns from the database
// you will actually use after this query.
String[] projection = {
    BaseColumns._ID,
    FeedEntry.COLUMN_NAME_TITLE,
    FeedEntry.COLUMN_NAME_SUBTITLE
    };

// Filter results WHERE "title" = 'My Title'
String selection = FeedEntry.COLUMN_NAME_TITLE + " = ?";
String[] selectionArgs = { "My Title" };

// How you want the results sorted in the resulting Cursor
String sortOrder =
    FeedEntry.COLUMN_NAME_SUBTITLE + " DESC";

Cursor cursor = db.query(
    FeedEntry.TABLE_NAME,   // The table to query
    projection,             // The array of columns to return (pass null to get all)
    selection,              // The columns for the WHERE clause
    selectionArgs,          // The values for the WHERE clause
    null,                   // don't group the rows
    null,                   // don't filter by row groups
    sortOrder               // The sort order
    );

Das dritte und vierte Argument (selection und selectionArgs) sind kombiniert, um eine WHERE-Klausel zu erstellen. Weil die Argumente getrennt von der Auswahl bereitgestellt werden werden sie vor der Zusammenführung mit Escapezeichen versehen. Dadurch werden Ihre Auswahlanweisungen gegen SQL immun Injektion. Weitere Informationen zu allen Argumenten finden Sie in der Referenz zu query().

Wenn du dir eine Zeile im Cursor ansehen möchtest, verwende eine der Cursor-Bewegungen , die Sie immer aufrufen müssen, bevor Sie mit dem Lesen von Werten beginnen. Da der Cursor bei Position -1 wird durch das Aufrufen von moveToNext() die „Leseposition“ festgelegt. am ersten Eintrag in den Ergebnissen und gibt zurück, ob der Cursor bereits nach dem letzten Eintrag in in der Ergebnismenge. Für jede Zeile können Sie den Wert einer Spalte lesen, indem Sie einen der Cursor Get-Methoden wie getString() oder getLong(). Für jede der get-Methoden, müssen Sie die Indexposition der gewünschten Spalte übergeben. Diese erhalten Sie durch Aufrufen getColumnIndex() oder getColumnIndexOrThrow(). Nach Abschluss Ergebnisse iterieren, rufen Sie close() am Cursor auf um seine Ressourcen freizugeben. Das folgende Beispiel zeigt, wie alle Element-IDs abgerufen werden, die in einem Cursor gespeichert sind und fügen sie einer Liste hinzu:

Kotlin

val itemIds = mutableListOf<Long>()
with(cursor) {
    while (moveToNext()) {
        val itemId = getLong(getColumnIndexOrThrow(BaseColumns._ID))
        itemIds.add(itemId)
    }
}
cursor.close()

Java

List itemIds = new ArrayList<>();
while(cursor.moveToNext()) {
  long itemId = cursor.getLong(
      cursor.getColumnIndexOrThrow(FeedEntry._ID));
  itemIds.add(itemId);
}
cursor.close();

Informationen aus einer Datenbank löschen

Um Zeilen aus einer Tabelle zu löschen, müssen Sie Auswahlkriterien angeben, Identifizieren Sie die Zeilen für die Methode delete(). Die funktioniert genauso wie die Auswahlargumente query()-Methode. Sie teilt die Auswahlspezifikation in eine Auswahlklausel und Auswahlargumente. Die definiert die zu betrachtenden Spalten und ermöglicht Ihnen, Spalten zu kombinieren, Tests durchführen. Die Argumente sind Werte, gegen die geprüft werden soll und die an die Klausel gebunden sind. Da das Ergebnis nicht wie bei einer regulären SQL-Anweisung gehandhabt wird, immun gegen SQL-Injection.

Kotlin

// Define 'where' part of query.
val selection = "${FeedEntry.COLUMN_NAME_TITLE} LIKE ?"
// Specify arguments in placeholder order.
val selectionArgs = arrayOf("MyTitle")
// Issue SQL statement.
val deletedRows = db.delete(FeedEntry.TABLE_NAME, selection, selectionArgs)

Java

// Define 'where' part of query.
String selection = FeedEntry.COLUMN_NAME_TITLE + " LIKE ?";
// Specify arguments in placeholder order.
String[] selectionArgs = { "MyTitle" };
// Issue SQL statement.
int deletedRows = db.delete(FeedEntry.TABLE_NAME, selection, selectionArgs);

Der Rückgabewert für die Methode delete() gibt die Anzahl der Zeilen an, die aus der Datenbank gelöscht wurden.

Datenbank aktualisieren

Wenn Sie eine Teilmenge der Datenbankwerte ändern müssen, verwenden Sie die Methode update()-Methode.

Beim Aktualisieren der Tabelle wird die ContentValues-Syntax von insert() mit der Syntax WHERE von delete().

Kotlin

val db = dbHelper.writableDatabase

// New value for one column
val title = "MyNewTitle"
val values = ContentValues().apply {
    put(FeedEntry.COLUMN_NAME_TITLE, title)
}

// Which row to update, based on the title
val selection = "${FeedEntry.COLUMN_NAME_TITLE} LIKE ?"
val selectionArgs = arrayOf("MyOldTitle")
val count = db.update(
        FeedEntry.TABLE_NAME,
        values,
        selection,
        selectionArgs)

Java

SQLiteDatabase db = dbHelper.getWritableDatabase();

// New value for one column
String title = "MyNewTitle";
ContentValues values = new ContentValues();
values.put(FeedEntry.COLUMN_NAME_TITLE, title);

// Which row to update, based on the title
String selection = FeedEntry.COLUMN_NAME_TITLE + " LIKE ?";
String[] selectionArgs = { "MyOldTitle" };

int count = db.update(
    FeedReaderDbHelper.FeedEntry.TABLE_NAME,
    values,
    selection,
    selectionArgs);

Der Rückgabewert der Methode update() ist Anzahl der betroffenen Zeilen in der Datenbank.

Datenbankverbindung wird bestehen bleiben

Seit getWritableDatabase() und getReadableDatabase() sind wenn die Datenbank geschlossen ist, sollten Sie Ihre Datenbankverbindung solange Sie darauf zugreifen müssen. In der Regel ist es optimal, die Datenbank zu schließen in der onDestroy() der aufrufenden Aktivität.

Kotlin

override fun onDestroy() {
    dbHelper.close()
    super.onDestroy()
}

Java

@Override
protected void onDestroy() {
    dbHelper.close();
    super.onDestroy();
}

Datenbankfehler beheben

Das Android SDK enthält ein sqlite3-Shell-Tool, mit dem du Inhaltsverzeichnis, SQL-Befehle und weitere nützliche Funktionen auf SQLite ausführen. Datenbanken. Weitere Informationen finden Sie unter Shell-Befehle ausführen.