T#011 – Aggiornare un database sqlite in un’applicazione Android esistente

In un precedente tutorial abbiamo visto come creare un database sqlite per la nostra applicazione Android, ma cosa occorre gestire nel caso in cui in un aggiornamento della nostra applicazione vogliamo modificare la struttura del database? Per fortuna Android ci aiuta in questo delicato compito mettendo a nostra disposizione un metodo che viene richiamato quando la versione del database cambia, vediamo insieme di cosa si tratta con un esempio pratico.

Per comodità iniziamo il nostro tutorial partendo proprio dal precedente tutorial, apriamo il progetto e prima di fare qualsiasi cosa cambiamo la versione del nostro database. Nel progetto avevamo impostato il valore 1, incrementiamo quindi il campo e settiamolo a 2. La versione viene passata al costruttore di SQLiteOpenHelper:

1
2
3
4
5
6
private static final int SCHEMA_VERSION = 2;
 
public DatabaseHelper(Context context)
{
	super(context, DATABASE_NAME, null, SCHEMA_VERSION);
}

Il metodo onUpgrade

Il metodo fondamentale in questi casi è onUpgrade di SQLiteOpenHelper, viene richiamato in automatico quando la versione del database cambia, ovvero quando il valore passato al costruttore è diverso rispetto all’ultima volta (solitamente rispetto all’ultimo avvio dell’applicazione). I parametri di questo metodo sono tre e sono: il database, la vecchia versione e la nuova versione:

1
2
3
4
5
6
7
8
9
10
11
12
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
{
	if (oldVersion < 2)
	{
		creaTabellaRegioni(db);
		aggiungiColonnaRegione(db);
 
		inserisciRegioni(db);
		aggiornaProvince(db);
	}
}

In questo esempio controlliamo se la nuova versione è minore di 2, in caso affermativo viene creata una nuova tabella, aggiunta una colonna alla tabella esistente e popolata con i dati aggiuntivi. In questo metodo dobbiamo gestire anche il caso in cui ci siano più aggiornamenti di versione in contemporanea, questo può succedere se l’applicazione non viene sempre aggiornata dall’utente. Solitamente questo metodo contiene una serie di if in sequenza simili a quello appena visto, in ogni if si controlla una versione diversa.

A voler essere pignoli si dovrebbe gestire anche il cambio di versione “multiplo”, che succede infatti se un utente installa in sequenza l’applicazione con la versione 1, 2, 1 e infine di nuovo la 2? La seconda volta che viene eseguito l’aggiornamento del database viene lanciata una eccezione in quanto le tabelle che cerchiamo di creare esistono già!

Questo caso succede solo se un utente reinstalla una versione vecchia dell’applicazione (senza quindi passare dal market), per questo motivo dovrebbe essere poco frequente.

Cambiamenti delle tabelle

In questo esempio l’aggiornamento del database consiste nell’aggiunta della tabella delle province e nella corrispondente aggiunta della colonna id_regione nella tabella delle province. Aggiungiamo il campo nell’interfaccia Java che contiene le colonne della tabella delle province:

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

Creiamo una ulteriore interfaccia con le informazioni della tabella delle regioni (in questo caso mettiamo solo una colonna con il nome):

1
2
3
4
5
6
public interface RegioneTable extends BaseColumns
{
	String TABLE_NAME = "regioni";
 
	String NOME = "nome";
}

Il metodo creaTabellaRegioni esegue semplicemente una istruzione sql create table:

1
2
3
4
5
6
7
private void creaTabellaRegioni(SQLiteDatabase db)
{
	String sqlRegioni = "CREATE TABLE {0} ({1} INTEGER PRIMARY KEY AUTOINCREMENT, " + 
		"{2} TEXT NOT NULL);";
	db.execSQL(MessageFormat.format(sqlRegioni, RegioneTable.TABLE_NAME,
		RegioneTable._ID, RegioneTable.NOME));
}

Aggiungiamo anche la colonna nella tabella delle province usando una semplice istruzione sql alter table in quanto la tabella esiste già sul database:

1
2
3
4
5
6
private void aggiungiColonnaRegione(SQLiteDatabase db)
{
	String sqlProvince = "ALTER TABLE {0} ADD COLUMN {1} INTEGER;";
	db.execSQL(MessageFormat.format(sqlProvince, ProvinciaTable.TABLE_NAME,
		ProvinciaTable.REGIONE));
}

Un db administrator rimarrebbe scioccato dal fatto che non abbiamo creato anche la “foreign key”, visto che questo non è un corso sql facciamo finta di niente e andiamo avanti! Se però siete dei db administrator e volete fare le cose per bene potete creare la foreign key eseguendo l’istruzione sql corrispondente usando il metodo execSQL.

Popolamento dei dati

L’inserimento delle regioni non lo vediamo in questo articolo in quanto è molto simile all’inserimento delle province del precedente tutorial. Vediamo invece l’aggiornamento della tabella delle province, in questo caso eseguiamo un comando sql (per farci perdonare dal db administrator di prima) invece di usare la classe ContentValues:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private void aggiornaProvincia(SQLiteDatabase db, String codice, String regione)
{
	db.execSQL("update " + ProvinciaTable.TABLE_NAME + " set " +
		ProvinciaTable.REGIONE + " = (select _id from " + 
		RegioneTable.TABLE_NAME + " where " + RegioneTable.NOME +
		" = ?) where " + ProvinciaTable.CODICE + " = ?",
		new Object[] { regione, codice });
}
 
private void aggiornaProvince(SQLiteDatabase db)
{
	aggiornaProvincia(db, "AG", "Sicilia");
	//...
	aggiornaProvincia(db, "VT", "Lazio");
}

Query aggiornata

Aggiorniamo anche il metodo che esegue una query sul database, anche in questo caso usiamo una query sql richiamando il metodo rawQuery:

1
2
3
4
5
6
public Cursor getProvince(int length)
{
	String query = "select p.codice, p.nome from province p inner join regioni r " + 
		"on p.id_regione = r._id where r.nome = ? order by p.codice";
	return getReadableDatabase().rawQuery(query, new String[] { "Toscana" });
}

Ora che abbiamo il nostro db aggiornato contenente i nostri bei dati, non ci resta che sfruttare tutto questo per popolare una ListView, ma questo lo vedremo nel prossimo tutorial!

Nel frattempo potete scaricare come al solito i sorgenti del progetto ed importarlo nel vostro ambiente di sviluppo Eclipse.

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.

1 comment

Leave a comment

Your email address will not be published.