T#009 – Creare ed usare database SQLite nelle applicazioni Android

Dopo aver visto la gestione delle preferenze vediamo come gestire il caso in cui abbiamo bisogno di salvare dei dati strutturati all’interno di un database nella nostra applicazione. In questo tutorial di programmazione Android vedremo come creare un database sqlite sul nostro device Android, come inserire dei dati ed eseguire semplici query.

Iniziamo a definire una tabella nel nostro database; Android utilizza internamente (ad esempio per i contatti e gli sms) e mette a disposizione degli sviluppatori, un database sqlite, non c’è bisogno di aggiungere librerie esterne nè di fare cose particolari per utilizzarlo.

Usiamo lo stesso stile utilizzato all’interno del core di Android, le colonne della tabella che vogliamo creare le raggruppiamo in una interfaccia java che estende BaseColumns:

1
2
3
4
5
6
7
8
9
10
11
public interface ProvinciaTable extends BaseColumns
{
	String TABLE_NAME = "province";
 
	String CODICE = "codice";
 
	String NOME = "nome";
 
	String[] COLUMNS = new String[]
	{ _ID, CODICE, NOME };
}

Un’interfaccia Java contiene solitamente una serie di metodi, quando contiene un campo, questo è una costante (è implicitamente public static final). Questa interfaccia ci serve per non dover stare a scrivere in più punti i nomi delle colonne (rischiando errori di digitazione) e per organizzare al meglio il nostro codice (per sapere come è fatta la tabella basta guardare la relativa interfaccia). BaseColumns è anch’essa una interfaccia Java definita nel core di Android e contiene il campo _ID, in molte occasioni Android si aspetta di avere una colonna _ID e quindi è conveniente includere sempre questa colonna nelle nostre tabelle.

Scriviamo quindi la classe che si occuperà di creare il nostro database. Estendiamo la classe SQLiteOpenHelper e scriviamo il costruttore:

1
2
3
4
5
6
7
8
9
10
11
12
public class DatabaseHelper extends SQLiteOpenHelper
{
	private static final String DATABASE_NAME = "devAPP.db";
 
	private static final int SCHEMA_VERSION = 1;
 
	public DatabaseHelper(Context context)
	{
		super(context, DATABASE_NAME, null, SCHEMA_VERSION);
	}
	//...
}

La classe SQLiteOpenHelper si occupa di gestire il database, in particolare crea il db la prima volta che viene usato, permette di gestire gli aggiornamenti e gestisce le connessioni. Nel costruttore abbiamo passato il nome del database e la versione attuale (passiamo 1 in quanto è la nostra prima versione, nel prossimo post vedremo come gestire gli aggiornamenti).

Per gestire la creazione del database riscriviamo il metodo onCreate, il parametro passato a questo metodo è il database appena creato (possiamo notare che non abbiamo dovuto fare niente per creare il database, ha fatto tutto il framework di Android per noi!). Scriviamo il codice di creazione della tabella delle province (i nomi delle colonne sono prese dall’interfaccia vista all’inizio di questo post) e di inserimento dei dati:

1
2
3
4
5
6
7
8
9
10
11
12
public void onCreate(SQLiteDatabase db)
{
	String sql = "CREATE TABLE {0} ({1} INTEGER PRIMARY KEY AUTOINCREMENT," + 
		" {2} TEXT NOT NULL,{3} TEXT NOT NULL);";
	db.execSQL(MessageFormat.format(sql, ProvinciaTable.TABLE_NAME, ProvinciaTable._ID,
		ProvinciaTable.CODICE, ProvinciaTable.NOME));
 
	inserisciProvincia(db, "Agrigento", "AG");
	inserisciProvincia(db, "Alessandria", "AL");
	//...
	inserisciProvincia(db, "Viterbo", "VT");
}

Il metodo inserisciProvincia esegue una insert sulla tabella delle province, invece di scrivere codice sql utilizziamo il metodo insert dell’oggetto SQLiteDatabase. Questo metodo accetta tre parametri: la tabella in cui inserire la riga, una stringa per poter inserire righe vuote (di solito valorizzato a null) e i valori da inserire. Per passare i valori è necessario usare un oggetto ContentValues che in pratica è una mappa che associa al nome di una colonna il rispettivo valore.

1
2
3
4
5
6
7
private void inserisciProvincia(SQLiteDatabase db, String nome, String codice)
{
	ContentValues v = new ContentValues();
	v.put(ProvinciaTable.CODICE, codice);
	v.put(ProvinciaTable.NOME, nome);
	db.insert(ProvinciaTable.TABLE_NAME, null, v);
}

Vediamo adesso come effettuare una query per leggere i dati dalla tabella che abbiamo creato e popolato. Per avere una connessione al database è necessario chiamare getReadableDatabase() o getWritableDatabase() (a seconda se vogliamo un db in sola lettura o in lettura e scrittura) dentro la nostra classe che estende SQLiteOpenHelper. Sull’oggetto ritornato chiamiamo il metodo query passando i parametri:

  • nome della tabella
  • array dei nomi delle colonne da ritornare
  • filtro da applicare ai dati
  • argomenti su cui filtrare i dati (nel caso in cui nel filtro siano presenti parametri)
  • group by da eseguire
  • clausola having da usare
  • ordinamento da applicare ai dati

Questi concetti sono abbastanza familiari se conoscete già il linguaggio sql, se non lo conoscete aspettate fiduciosi uno dei prossimi tutorial che spiegherà qualche concetto base di sql!

Il metodo query ritorna un oggetto Cursor, questa classe è fondamentale per accedere ai dati di un database: permette di scorrere i dati ritornati da una query e di ottenere i valori delle varie colonne.
Il nostro metodo che ritorna le province con il nome più lungo di 10 caratteri è il seguente:

1
2
3
4
5
6
7
8
9
10
11
public Cursor getProvince()
{
	return (getReadableDatabase().query(
		ProvinciaTable.TABLE_NAME, 
		ProvinciaTable.COLUMNS, 
		"length(" + ProvinciaTable.NOME + ") > 10", 
		null,
		null, 
		null, 
		ProvinciaTable.NOME));
}

Creiamo una Activity che cicla sulle province e le stampa sul log: il ciclo sul cursore viene effettuato grazie al metodo moveToNext che sposta il cursore di una posizione e ritorna false quando il cursore è arrivato alla fine dei dati. Ogni cursore deve essere chiuso al termine dell’utilizzo; la chiusura viene effettuata invocando il metodo close e deve essere messa in un blocco finally. Il blocco finally è un costrutto java da usare insieme a un blocco try/catch, in pratica il codice dentro il blocco catch viene invocato quando c’è una eccezione mentre il codice dentro il finally viene invocato sia nel caso di normale esecuzione che nel caso di eccezione.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public void onCreate(Bundle savedInstanceState)
{
	super.onCreate(savedInstanceState);
	setContentView(R.layout.main);
	DatabaseHelper databaseHelper = new DatabaseHelper(this);
	Cursor c = databaseHelper.getProvince();
	try
	{
		while (c.moveToNext())
		{
			Log.d("devAPP", c.getLong(0) + " " + 
				c.getString(1) + " " + c.getString(2));
		}
	}
	finally
	{
		c.close();
	}
}

La prima volta che viene avviata l’applicazione il database verrà creato automaticamente, ai successivi riavvi il database sarà usato solo per leggere i dati. I log sono visibili nel LogCat del device, quando siamo in sviluppo dentro Eclipse è possibile vedere i log dentro la view di Eclipse LogCat (per mostrarla se non è visibile basta selezionare il menu Window e poi show view –> LogCat):



Se questa view è vuota probabilmente è perchè dobbiamo selezionare il device giusto nella view Device di Eclipse, possono essere presenti più device (o emulatori) contemporaneamente e LogCat mostra i log solo di un device alla volta.

I sorgenti di questo esempio sono disponibili per il download, nei prossimi tutorial vedremo come gestire le versioni del database e come collegare i dati a una ListView.

By Fabio Collini

Da agosto 2009 sono un freelance android developer, ho rilasciato due applicazioni nell'Android Market: Apps Organizer e Folder Organizer. Presso OmniaGroup ricopro il ruolo di Tech Leader nell'ambito di un progetto di rich internet application che utilizza JSF, JPA(EclipseLink) ed EJB3.

17 comments

  1. Salve, innanzitutto grazie per le vostre guide mi sono state molto utili…
    In questo esempio i dati vengono inseriti alla creazione del db, ma se volessi inserire dati da un’altra activity?

    Vi ringrazio in anticipo!

    1. Ciao, per accedere al dababase da un’altra activity le cose da fare sono sempre le stesse, usando lo stesso DatabaseHelper accedi allo stesso database.
      Il modo più semplice è creare una istanza di DatabaseHelper (che alla fine corrisponde a una connessione) per ogni activity che accede al database.

  2. Ciao Fabio e complimenti.

    Un chiarimento: se oltre alle Province volessi gestire ad esempio anche i Comuni dovrei:
    1) Creare altre due classi gemelle di DatabaseHelper e ProvinciaTable (tipo: ComuneTable e DatabaseComuneHelper)
    oppure
    2) Crearne una sola nuova ComuneTable ed implementare la DatabaseHelper con anche la gestione dei comuni.

    Cosa mi consigli? Indirizzami sono ‘un nuovo’ di Android.

  3. Salve,

    ma in caso dovessi effettuare una update su un dato specifico, e il dato non esistesse, esiste un modo per intercettare questo tipo di errori? Esempio una insert fallita per record già esistente, o una update fallita per record non trovato?

    Grazie infinite

  4. Ciao, il modo più semplice è usare il metodo update che ritorna un int che indica il numero di righe aggiornate. Nel caso in cui sia 0 puoi fare l’update

  5. Ciao,
    io ho un problema, non mi entra nel onCreate(); arriva alla chiamata del super, ma poi non passa nell’onCreate ed ovviamente fallisce la insert. Qualche idea? grazie

  6. ragazzi è possibile mettere il link per la sezione successiva e la precedente?
    la pagina è piena di banner e ne compare sempre uno popup in basso ogni volta che navigo per cercare la lezione successiva ed è snervante
    anche il link per la precedente non c’è
    fate come hanno fatto nel sito html.it

  7. salve,
    se nel mio db devo inserire stringhe con più di mille caratteri come risolvo questo problema?

  8. Grazie per il tutorial, ma non capisco se con android si può rilasciare la app con un db già esistente o se bisogna per forza sempre crearlo, perchè finchè sono pochi i dati altrimenti …
    Grazie!

  9. già vorrei sapere anche io se c’è un modo alternativo per importare una grande quantità di dati…


    Thanks

  10. Ciao utilissimo tutorial…solo che ora sono bloccato, vorrei inserire un campo data con l’inserimento tramite datepicker, come posso fare???
    Grazie

  11. Il tutorial è stato di grande aiuto! Ora ho un problema… Ho creato un database dove vado ad inserire dei dati (una rubrica) e quindi ho riempito il database. Dopo di che attraverso un bottone che io ho chiamato “cerca” mi scorro ad esempio tutti i nomi che sono stati aggiunti nel database. Quando scorro la lista dei nomi inseriti, vorrei aprire una nuova activity al touch di un nome dell’elenco per visualizzare tutte le informazioni inserite per quel singolo nome. Come posso fare????


  12. Lucy:

    Il tutorial è stato di grande aiuto! Ora ho un problema… Ho creato un database dove vado ad inserire dei dati (una rubrica) e quindi ho riempito il database. Dopo di che attraverso un bottone che io ho chiamato “cerca” mi scorro ad esempio tutti i nomi che sono stati aggiunti nel database. Quando scorro la lista dei nomi inseriti, vorrei aprire una nuova activity al touch di un nome dell’elenco per visualizzare tutte le informazioni inserite per quel singolo nome. Come posso fare????

Leave a comment

Your email address will not be published.