Salvare i dati in un database è ideale per la ripetizione o i dati strutturati,
come i dati di contatto. Questa pagina presuppone che tu stia
che ha familiarità con i database SQL in generale e ti aiuta a iniziare con
Database SQLite su Android. Le API necessarie per utilizzare un database
su Android sono disponibili nel pacchetto android.database.sqlite
.
Attenzione: anche se queste API sono potenti, sono piuttosto di basso livello e richiedono molto tempo e un grande impegno:
- Non esiste una verifica in fase di compilazione delle query SQL non elaborate. Poiché i tuoi dati devi aggiornare manualmente le query SQL interessate. Questo può richiedere molto tempo e causare errori.
- Devi utilizzare molto codice boilerplate per la conversione tra query SQL e oggetti dati.
Per questi motivi, ti consigliamo vivamente di utilizzare Libreria sulla persistenza della stanza come livello di astrazione per accedere alle informazioni nella SQLite dell'app o Microsoft SQL Server.
Definisci uno schema e un contratto
Uno dei principi principali dei database SQL è lo schema: una dichiarazione di organizzazione del database. Lo schema si riflette nella query istruzioni usate per creare il database. Potrebbe essere utile crea una classe companion, detta classe contract, che specifica il layout dello schema in modo sistematico e auto-documentante.
Una classe contratto è un container di costanti che definiscono i nomi degli URI, tabelle e colonne. La classe di contratto ti consente di utilizzare le stesse costanti in tutte le altre classi nello stesso pacchetto. In questo modo puoi modificare una colonna in un unico posto e fare in modo che si propaghi nel codice.
Un buon modo per organizzare una classe contratto è quello di inserire definizioni a livello globale all'intero database nel livello principale della classe. Quindi crea una finestra per ogni tabella. Ogni classe interna enumera le colonne della tabella corrispondente.
Nota: se implementi BaseColumns
a riga di comando, la classe interna può ereditare una
denominato _ID
, che alcune classi di Android come CursorAdapter
si aspettano di avere. Non è obbligatorio, ma può aiutare il tuo database
lavorare in modo armonioso con il framework Android.
Ad esempio, il contratto riportato di seguito definisce il nome della tabella e dei nomi delle colonne per singola tabella che rappresenta un feed 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"; } }
Crea un database utilizzando un helper SQL
Una volta definito l'aspetto del database, devi implementare dei metodi che creano e gestiscono il database e le tabelle. Ecco alcuni esempi che creano ed eliminano una tabella:
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;
Come i file che salvi nell'account interno spazio di archiviazione, Android archivia il database nella cartella privata dell'app. I tuoi dati sono perché per impostazione predefinita quest'area non è accessibile ad altre app o all'utente.
Il corso SQLiteOpenHelper
contiene un'utile
di API per la gestione del database.
Quando utilizzi questa classe per ottenere riferimenti al tuo database, il sistema
esegue potenzialmente
di creare e aggiornare il database solo quando
e non durante l'avvio dell'app. Non devi fare altro che chiamare
getWritableDatabase()
o
getReadableDatabase()
.
Nota: poiché possono essere di lunga durata,
assicurati di chiamare getWritableDatabase()
o getReadableDatabase()
in un thread in background.
Per ulteriori informazioni, vedi Threading su Android.
Per utilizzare SQLiteOpenHelper
, crea una sottoclasse
esegue l'override di onCreate()
e
onUpgrade()
metodi di callback. Puoi
anche implementare
onDowngrade()
o
onOpen()
metodi,
ma non sono obbligatori.
Ad esempio, ecco un'implementazione di SQLiteOpenHelper
che
utilizza alcuni dei comandi mostrati sopra:
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); } }
Per accedere al database, crea un'istanza della sottoclasse
SQLiteOpenHelper
:
Kotlin
val dbHelper = FeedReaderDbHelper(context)
Java
FeedReaderDbHelper dbHelper = new FeedReaderDbHelper(getContext());
Inserire le informazioni in un database
Inserisci i dati nel database passando un valore ContentValues
il metodo 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);
Il primo argomento per insert()
è semplicemente il nome della tabella.
Il secondo argomento indica al framework cosa fare nel caso in cui
Il campo ContentValues
è vuoto (ossia non l'hai fatto
put
qualsiasi valore).
Se specifichi il nome di una colonna, il framework inserisce una riga e imposta
il valore di quella colonna su null. Se specifichi null
, come in questo
nell'esempio di codice, il framework non inserisce una riga se non sono presenti valori.
Il metodo insert()
restituisce l'ID per
appena creata o restituirà -1 se si è verificato un errore durante l'inserimento dei dati. Ciò può accadere
in caso di conflitto con dati preesistenti nel database.
Legge le informazioni da un database
Per leggere da un database, utilizza il metodo query()
, passando i criteri di selezione e le colonne desiderate.
Il metodo combina gli elementi di insert()
e update()
, tranne l'elenco di colonne
definisce i dati da recuperare (la "proiezione") anziché i dati da inserire. I risultati
della query ti vengono restituiti in un oggetto 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 );
Il terzo e il quarto argomento (selection
e selectionArgs
) sono
combinato per creare una clausola WHERE. Poiché gli argomenti vengono forniti separatamente dalla selezione
query, vengono sottoposti a escape prima di essere combinati. Ciò rende le istruzioni di selezione immuni a SQL
iniezione di codice. Per maggiori dettagli su tutti gli argomenti, consulta
Riferimento query()
.
Per guardare una riga nel cursore, usa uno degli spostamenti Cursor
, che devi sempre chiamare prima di iniziare a leggere i valori. Poiché il cursore parte da
posizione -1, la chiamata di moveToNext()
consente di ottenere la "posizione di lettura" il
la prima voce nei risultati e restituisce se il cursore ha già superato l'ultima voce in
l'insieme di risultati. Per ogni riga, puoi leggere il valore di una colonna richiamando uno dei
Cursor
metodi get, come getString()
o getLong()
. Per ognuno dei metodi get,
devi passare la posizione di indice della colonna desiderata, che puoi ottenere chiamando
getColumnIndex()
o
getColumnIndexOrThrow()
. Al termine
eseguendo l'iterazione dei risultati, richiama close()
sul cursore
per rilasciare le sue risorse.
Ad esempio, di seguito viene mostrato come recuperare tutti gli ID elemento archiviati in un cursore
e aggiungerli a un elenco:
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();
Elimina informazioni da un database
Per eliminare le righe da una tabella, devi fornire criteri di selezione che
identificare le righe nel metodo delete()
. La
funziona allo stesso modo degli argomenti di selezione
query()
. Divide
la specifica della selezione in una clausola e gli argomenti di selezione. La
definisce le colonne da esaminare e consente anche di combinare le colonne
test. Gli argomenti sono valori da verificare che sono associati alla clausola.
Poiché il risultato non viene gestito come una normale istruzione SQL,
immuni all'iniezione 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);
Il valore restituito per il metodo delete()
indica il numero di righe eliminate dal database.
Aggiorna un database
Quando devi modificare un sottoinsieme dei valori del database, utilizza
update()
.
L'aggiornamento della tabella combina la sintassi ContentValues
di
insert()
con la sintassi WHERE
di 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);
Il valore restituito del metodo update()
è
il numero di righe interessate nel database.
Connessione al database permanente
Dal giorno getWritableDatabase()
e getReadableDatabase()
sono
costoso da chiamare quando il database è chiuso, dovresti lasciare la connessione al database
e sarà aperto tutto il tempo necessario per accedervi. In genere, è ottimale chiudere il database
in onDestroy()
dell'Attività di chiamata.
Kotlin
override fun onDestroy() { dbHelper.close() super.onDestroy() }
Java
@Override protected void onDestroy() { dbHelper.close(); super.onDestroy(); }
Esegui il debug del database
L'SDK Android include uno strumento shell di sqlite3
che ti consente di esplorare
contenuti della tabella, eseguire comandi SQL ed eseguire altre funzioni utili su SQLite
o Microsoft SQL Server. Per maggiori informazioni, scopri come inviare comandi della shell.