Zapisanie danych w bazie danych to idealne rozwiązanie do ich tworzenia powtórzonych lub uporządkowanych,
np. danych kontaktowych. Zakładamy na tej stronie, że
zna ogólnie bazy danych SQL i pomaga zacząć
Bazy danych SQLite na Androidzie. Interfejsy API potrzebne do korzystania z bazy danych
na Androida są dostępne w pakiecie android.database.sqlite
.
Uwaga: te interfejsy API są zaawansowane, ale są dość niskie. i wymagają dużo czasu i wysiłku:
- W przypadku nieprzetworzonych zapytań SQL nie można weryfikować nieprzetworzonych zapytań SQL w czasie kompilowania. Twoje dane zmian na wykresie, musisz ręcznie zaktualizować odpowiednie zapytania SQL. Ten może być czasochłonny i podatny na błędy.
- Do konwersji między zapytaniami SQL musisz używać dużego stałego kodu i obiekty danych.
Dlatego zdecydowanie zalecamy korzystanie z Biblioteka trwałości sal jako warstwy abstrakcyjnej umożliwiającej dostęp do informacji w kodzie SQLite aplikacji baz danych.
Zdefiniuj schemat i umowę
Jedną z głównych zasad baz danych SQL jest schemat, czyli formalny czyli deklaracji struktury bazy danych. Schemat jest odzwierciedlony w SQL za pomocą instrukcji tworzenia bazy danych. Może Ci się przydać utwórz klasę towarzyszącą, tzw. contract, która wyraźnie określa układ Twojego schematu w sposób systematyczny i samodokumentujący się.
Klasa umowy to kontener dla stałych, które definiują nazwy identyfikatorów URI, i kolumny. Klasa kontraktowa pozwala na użycie tych samych stałych dla wszystkich innych klas w tym samym pakiecie. Umożliwia to zmianę kolumny w jednym miejscu i propagować ją w całym kodzie.
Dobrym sposobem na uporządkowanie klasy umowy jest zastosowanie definicji, które globalny do całej bazy danych na poziomie głównym klasy. Następnie stwórz w sobie dla każdej tabeli. Każda klasa wewnętrzna wylicza odpowiednie kolumny tabeli.
Uwaga: dzięki wdrożeniu BaseColumns
klasa wewnętrzna może dziedziczyć
polu klucza o nazwie _ID
, którego oczekują niektóre klasy Androida, takie jak CursorAdapter
. Nie jest to wymagane, ale może to pomóc w Twojej bazie danych
współdziałają z platformą Android.
Na przykład ta umowa definiuje nazwę tabeli i nazwy kolumn dla pojedyncza tabela reprezentująca kanał RSS:
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"; } }
Tworzenie bazy danych za pomocą Asystenta SQL
Po zdefiniowaniu wyglądu bazy danych zaimplementuj metody które tworzą i utrzymują bazę danych oraz tabele. Oto kilka typowych instrukcje tworzenia i usuwania tabeli:
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;
Tak samo jak pliki zapisywane w pamięci wewnętrznej urządzenia , Android przechowuje bazę danych w prywatnym folderze aplikacji. Twoje dane są bezpieczeństwa, ponieważ domyślnie ten obszar nie jest które mogą być dostępne dla innych aplikacji lub dla użytkownika.
Klasa SQLiteOpenHelper
zawiera przydatne
interfejsów API do zarządzania bazą danych.
Gdy używasz tej klasy do uzyskania odwołań do bazy danych, system
przetwarza potencjalnie
długotrwałe operacje tworzenia i aktualizowania bazy danych tylko wtedy,
i nie podczas uruchamiania aplikacji. Wystarczy zadzwonić
getWritableDatabase()
lub
getReadableDatabase()
Uwaga: mogą one być długoterminowe,
pamiętaj o wywołaniu funkcji getWritableDatabase()
lub getReadableDatabase()
w wątku w tle.
Więcej informacji znajdziesz w artykule Threading na urządzeniach z Androidem.
Aby użyć klasy SQLiteOpenHelper
, utwórz podklasę, która
zastępuje onCreate()
i
onUpgrade()
metod wywołania zwrotnego. Możesz
chcą też wdrożyć
onDowngrade()
lub
onOpen()
metod,
ale nie są one wymagane.
Oto przykładowa implementacja SQLiteOpenHelper
, która
używa niektórych z powyższych poleceń:
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); } }
Aby uzyskać dostęp do bazy danych, utwórz instancję podklasy
SQLiteOpenHelper
:
Kotlin
val dbHelper = FeedReaderDbHelper(context)
Java
FeedReaderDbHelper dbHelper = new FeedReaderDbHelper(getContext());
Umieszczanie informacji w bazie danych
Wstaw dane do bazy danych, przekazując ContentValues
do metody insert()
:
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);
Pierwszy argument funkcji insert()
jest po prostu nazwą tabeli.
Drugi argument wskazuje systemowi, co należy zrobić w przypadku, gdy
Pole ContentValues
jest puste (np. nie zawierało
put
dowolną wartość).
Jeśli określisz nazwę kolumny, platforma wstawi wiersz i ustawi
wartość tej kolumny na null. Jeśli określisz null
, jak w tym
przykładowego kodu, platforma nie wstawia wiersza, jeśli nie ma wartości.
Metody insert()
zwracają identyfikator metody
nowo utworzonego wiersza. W przeciwnym razie zwraca wartość -1, jeśli podczas wstawiania danych wystąpił błąd. Może się tak zdarzyć
.
Odczytywanie informacji z bazy danych
Aby odczytać dane z bazy danych, użyj metody query()
, przekazując do niej kryteria wyboru i odpowiednie kolumny.
Metoda łączy elementy funkcji insert()
i update()
, z wyjątkiem listy kolumn
określa dane, które chcesz pobrać („odwzorowanie”), a nie dane do wstawienia. Wyniki
zapytań zostanie zwróconych w obiekcie Cursor
.
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 );
Trzeci i czwarty argument (selection
i selectionArgs
) to:
zostanie połączona w celu utworzenia klauzuli WHERE. Ponieważ argumenty są podawane niezależnie od wyboru
przed połączeniem za ich pomocą zmienia się znaczenie modyfikacji. Dzięki temu instrukcje wyboru są odporne na SQL
wstrzyknięcie kodu. Więcej informacji o wszystkich argumentach znajdziesz tutaj:
Odwołanie do: query()
.
Aby spojrzeć na wiersz w kursie, użyj jednego z Cursor
przesunięć
które należy zawsze wywoływać przed rozpoczęciem odczytu wartości. Ponieważ kursor zaczyna się od
pozycja -1, wywołanie funkcji moveToNext()
powoduje umieszczenie „pozycji czytania” w
pierwszy wpis w wynikach i zwraca, czy kursor jest już po ostatniej pozycji w polu
wyniki. Dla każdego wiersza możesz odczytać wartość kolumny, wywołując jedną z funkcji
Cursor
pobiera metody, takie jak getString()
lub getLong()
. Dla każdej metody get
musisz przekazać pozycję indeksu wybranej kolumny, którą można uzyskać, wywołując
getColumnIndex()
lub
getColumnIndexOrThrow()
Po zakończeniu
iteracja przy użyciu wyników, wywołaj funkcję close()
na kursorze
aby uwolnić zasoby.
Na przykład poniżej pokazujemy, jak przenieść wszystkie identyfikatory produktów zapisane w kursie.
i dodaj je do listy:
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();
Usuwanie informacji z bazy danych
Aby usunąć wiersze z tabeli, musisz podać kryteria wyboru, które:
identyfikować wiersze z metodą delete()
.
działa tak samo jak argumenty wyboru w funkcji
Metoda query()
. Dzieli on
specyfikacji wyboru do klauzuli wyboru i argumentów wyboru.
określa przegląd kolumn i umożliwia łączenie kolumn
testów. Argumenty to wartości powiązane z klauzulą, które są podstawą do testowania.
Wynik nie jest obsługiwany tak samo jak zwykła instrukcja SQL, dlatego jest
odporny na wstrzyknięcie kodu SQL.
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);
Wartość zwrócona dla metody delete()
.
wskazuje liczbę wierszy usuniętych z bazy danych.
Zaktualizowanie bazy danych
Aby zmodyfikować podzbiór wartości w bazie danych, użyj metody
Metoda update()
.
Aktualizowanie tabeli łączy składnię ContentValues
funkcji
insert()
ze składnią WHERE
z 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);
Wartość zwrócona przez metodę update()
to
liczbę wierszy w bazie danych, których to dotyczy.
Trwałe połączenie z bazą danych
Od getWritableDatabase()
i getReadableDatabase()
są
koszt połączenia po zamknięciu bazy danych, należy opuścić połączenie z bazą danych
pozostają otwarte tak długo, jak to konieczne. Zwykle najlepiej jest zamknąć bazę danych,
w onDestroy()
aktywności związanej z połączeniami.
Kotlin
override fun onDestroy() { dbHelper.close() super.onDestroy() }
Java
@Override protected void onDestroy() { dbHelper.close(); super.onDestroy(); }
Debugowanie bazy danych
Pakiet Android SDK zawiera narzędzie powłoki sqlite3
, które umożliwia przeglądanie
zawartość tabel, uruchamianie poleceń SQL i wykonywanie innych przydatnych funkcji w SQLite
baz danych. Więcej informacji znajdziesz w artykule o wydawaniu poleceń powłoki.