Gestire database SQLite con Adobe AIR usando Flash CS6 (terza parte)

4
Share:

Segue la terza e ultima parte del tutorial dedicato alle tecniche di base per dialogare con un database locale tramite un’applicazione AIR. Segnalo di seguito i link relativi alla prima e la seconda parte.
In questo ultimo appuntamento completeremo l’interfaccia e arricchiremo la classe documento con dei metodi dedicati alla scrittura e alla cancellazione dei dati. A questo indirizzo è possibile scaricare i sorgenti dell’esempio completo: Scarica la cartella.
Ci sarebbero ancora cose da dire sui databse locali e AIR, ma mi riservo di riprendere l’argomento più avanti.

Completiamo l’interfaccia utente

Per completare l’interfaccia sono stati inserito ulteriori oggetti interattivi nel documento Flash. Per la precisione, un componente in grado di mostrare i dati presenti nel database, dei campi di inserimento per poter aggiungere nuovi contatti e dei pulsati per inviare e cancellare dati. Apriamo il fil esempioDB.fla disponibile nella cartella dei sorgenti e analizziamo il contenuto del livello elementi. Come è possibile osservare, nella parte alta sono stati collocati tre campi di testo TLF modificabili, denominati campoNome, campoCognome e campoTelefono. Accanto a questi campi si trova un pulsante il cui nome di istanza è p1. Per inserire un nuovo contatto nella rubrica, l’utente non deve fare altro che compilare i campi e premere il pulsante.
Nella parte sottostante è stato inserito un componente DataGrd il cui nome di istanza è grid. Poiché questo tipo di componente consente di visualizzare una raccolta di dati in una griglia di righe, è l’ideale per visualizzare i modo veloce tutti i contatti della nostra rubrica. Inoltre, se il numero dei dati supera lo spazio verticale, offre il vantaggio di mostrare una barra scorrevole.
Se come me non siete abituati ad usare i componenti (non li amo particolarmente per motivi estetici e di conseguenza li uso pochissimo), potete optare per un ripasso veloce grazie a questo breve videotutorial di RiverCityGraphix.
In ogni caso, il componente usato nel tutorial è stato ridimensionato manualmente in modo da occupare più o meno il centro dello Stage. Tuttavia la posizione e le dimensioni usate non sono vincolanti.
Nella parte finale ci sono infine due pulsanti denominati rispettivamente: p2 e p3. Il primo cancella un singolo contatto selezionato e il secondo elimina tutta la rubrica.

La classe documento

Per poter completare il nostro applicativo, è necessario aggiungere le istruzioni relative ai compiti svolti dai nuovi oggetti posti sullo Stage. Come è possibile notare osservando il codice, sono stati aggiunte nuove proprietà e nuovi metodi al codice postato nell’articolo precedente.

package{
import flash.display.MovieClip;
import flash.display.NativeWindow;
import flash.desktop.NativeApplication;
import flash.events.MouseEvent;
import fl.controls.DataGrid;
import fl.data.DataProvider;
import flash.filesystem.File;
import flash.data.SQLConnection;
import flash.data.SQLStatement;
import flash.data.SQLResult;
import flash.data.SQLStatement;
import flash.events.SQLEvent;
import flash.errors.SQLError;
import flash.events.SQLErrorEvent;
public class MioDB extends MovieClip{
private var connect:SQLConnection;
private var creatabellaStmt:SQLStatement;
private var inserisciStmt:SQLStatement;
private var selezionaStmt:SQLStatement;
private var cancellaStmt:SQLStatement;
private var dbFile:File;
private var mioArray:Array=new Array();
public function MioDB(){
initDB();
sfondo.addEventListener(MouseEvent.MOUSE_DOWN,sposta);
p1.addEventListener(MouseEvent.MOUSE_DOWN,scriviTabella);
p2.addEventListener(MouseEvent.MOUSE_DOWN,cancellaRiga);
p3.addEventListener(MouseEvent.MOUSE_DOWN,cancellaTabella);
p4.addEventListener(MouseEvent.MOUSE_DOWN,chiudi);
campoNome.restrict=”a-zA-Z”;
campoCognome.restrict=”a-zA-Z”;
campoTelefono.restrict=”0-9″;
}
private function sposta(e:MouseEvent):void{
stage.nativeWindow.startMove();
}
private function initDB():void{
var folder:File=File.applicationStorageDirectory.resolvePath(“dati”);
folder.createDirectory();
dbFile=folder.resolvePath(“DBesempio.db”);
connect=new SQLConnection();
connect.addEventListener(SQLEvent.OPEN,apri);
connect.addEventListener(SQLErrorEvent.ERROR,errore);
connect.openAsync(dbFile);
}
private function apri(e:SQLEvent):void{
miocampo.text=”> Il database è aperto…”+”\n”;
creaTabella();
}
private function errore(e:SQLErrorEvent):void{
miocampo.appendText(String(e.error.message));
miocampo.appendText(String(e.error.details));
}
private function creaTabella():void{
creatabellaStmt=new SQLStatement();
creatabellaStmt.sqlConnection=connect;
var stSQL:String=
“CREATE TABLE IF NOT EXISTS rubrica(” +
” mioid INTEGER PRIMARY KEY AUTOINCREMENT, ” +
” nome TEXT, “+
” cognome TEXT, “+
” telefono INTEGER ” +
“)”;
creatabellaStmt.text=stSQL;
creatabellaStmt.addEventListener(SQLEvent.RESULT,scrivi);
creatabellaStmt.addEventListener(SQLErrorEvent.ERROR,errore);
creatabellaStmt.execute();
}
private function scrivi(e:SQLEvent):void{
miocampo.appendText(“> Compila i campi per inserire nuovi contatti…”+”\n”);
mostraDatiScritti(null);
}
private function mostraDatiScritti(e:SQLEvent):void{
campoNome.text=campoCognome.text=campoTelefono.text=””;
selezionaStmt=new SQLStatement();
selezionaStmt.sqlConnection=connect;
var stSQL:String=”SELECT mioid, nome, cognome, telefono FROM rubrica ORDER BY cognome ASC”;
selezionaStmt.text=stSQL;
selezionaStmt.addEventListener(SQLEvent.RESULT,passaDataGrid);
selezionaStmt.addEventListener(SQLErrorEvent.ERROR, errore);
selezionaStmt.execute();
}
private function passaDataGrid(e:SQLEvent):void{
var res:SQLResult=selezionaStmt.getResult();
mioArray=res.data;
var dp:DataProvider = new DataProvider(mioArray);
grid.dataProvider=dp;
grid.columns=[“nome”, “cognome”, “telefono”];
}
private function scriviTabella(e:MouseEvent):void{
if(campoNome.text!=””||campoCognome.text!=””||campoTelefono.text!=””){
inserisciStmt=new SQLStatement();
inserisciStmt.sqlConnection=connect;
var stSQL:String=”INSERT INTO rubrica (nome, cognome, telefono) “;
stSQL+= “VALUES (@nome, @cognome, @telefono)”;
inserisciStmt.parameters[“@nome”]=campoNome.text;
inserisciStmt.parameters[“@cognome”]=campoCognome.text;
inserisciStmt.parameters[“@telefono”]=Number(campoTelefono.text);
inserisciStmt.text = stSQL;
inserisciStmt.addEventListener(SQLEvent.RESULT,mostraDatiScritti);
inserisciStmt.addEventListener(SQLErrorEvent.ERROR,errore);
inserisciStmt.execute();
}
}
private function cancellaRiga(e:MouseEvent):void{
for(var n:int=0;n<mioArray.length;n++){
if(grid.isItemSelected(grid.getItemAt(n))){
cancellaStmt=new SQLStatement();
cancellaStmt.sqlConnection = connect;
var stSQL:String=”DELETE FROM rubrica WHERE mioid = ?”;
cancellaStmt.text=stSQL;
cancellaStmt.parameters[0]=grid.selectedItem.mioid;
cancellaStmt.addEventListener(SQLEvent.RESULT, mostraDatiScritti);
cancellaStmt.execute();
}
}
}
private function cancellaTabella(e:MouseEvent):void{
cancellaStmt=new SQLStatement();
cancellaStmt.sqlConnection=connect;
var stSQL:String=”DROP TABLE IF EXISTS rubrica”;
cancellaStmt.text=stSQL;
cancellaStmt.execute();
mioArray=[];
var dpVuoto:DataProvider=new DataProvider(mioArray);
grid.columns=[“nome”, “cognome”, “telefono”];
grid.dataProvider=dpVuoto;
miocampo.appendText(“> La rubrica è stata cancellata…”+”\n”);
creaTabella();
}
private function chiudi(e:MouseEvent):void{
connect.close();
NativeApplication.nativeApplication.exit();
}
}
}

Il listato è decisamente ingrassato: ha messo su ben 139 righe contro le precedenti 49. E’ anche vero che, pur essendo il codice per creare un piccola applicazione simbolica, stiamo pur sempre dialogando con un database. Vediamo di “spezzettare” la classe in modo da analizzare poco alla volta i vari blocchi di codice inediti.
Prima di tutto, nella parte iniziale del codice posta dopo la dichiarazione di classe, sono state create quattro nuove proprietà private corrispondenti ad altrettante istanze della classe SQLStatement.

private var creatabellaStmt:SQLStatement;
private var inserisciStmt:SQLStatement;
private var selezionaStmt:SQLStatement;
private var cancellaStmt:SQLStatement;

La classe SQLStatement serve ad eseguire delle istruzioni SQL su un database. Nel nostro listato i quattro oggetti SQLStatement si occuperanno di: creare la tabella se non esiste,  inserire nuovi dati, individuare i dati inseriti e cancellare i dati.
Tra le nuove istruzioni presenti nella parte iniziale del codice abbiamo anche la creazione di un array che ci servirà a inviare i contatti della rubrica al componente DataGrid.
Passiamo quindi alla funzione costruttore  MioDB(), che in questa versione del listato mostra qualche riga di codice in più.

public function MioDB(){
initDB();
sfondo.addEventListener(MouseEvent.MOUSE_DOWN,sposta);
p1.addEventListener(MouseEvent.MOUSE_DOWN,scriviTabella);
p2.addEventListener(MouseEvent.MOUSE_DOWN,cancellaRiga);
p3.addEventListener(MouseEvent.MOUSE_DOWN,cancellaTabella);
p4.addEventListener(MouseEvent.MOUSE_DOWN,chiudi);
campoNome.restrict=”a-zA-Z”;
campoCognome.restrict=”a-zA-Z”;
campoTelefono.restrict=”0-9″;
}

In corrispondenza dei tre pulsanti collocati sullo Stage, sono attribuiti tre funzioni listener: scriviTabella(), cancellaRiga() e cancellaTabella(). I nomi delle funzioni sono abbastanza intuitivi per cui è facile capire a cosa servono. Nella funzione costruttore sono anche state settate le proprietà restrict dei tre campi TLF modificabili. In particolare, viene fatto in modo che i primi campi accettino solo lettere (maiuscole oppure minuscole) che il terzo campo accetti solo valori numerici.In questo modo impediamo agli utenti di inserire dati errati durante la digitazione.
Una volta analizzata la funzione costruttore passiamo al codice eseguito automaticamente ad ogni avvio dell’applicazione. Nella funzione listener apri(), abbiamo la chiamata della funzione creaTabella() assente nella precedente versione del codice.
Come abbiamo visto la volta scorsa, la funzione apri() si innesca non appena si apre la connessione con il database. La creazione della tabella è la prima istruzione da eseguire per poter concretamente interagire con il file .DB. Per comodità riporto di seguito il codice della funzione creaTabella():

private function creaTabella():void{
creatabellaStmt=new SQLStatement();
creatabellaStmt.sqlConnection=connect;
var stSQL:String=
“CREATE TABLE IF NOT EXISTS rubrica(” +
” mioid INTEGER PRIMARY KEY AUTOINCREMENT, ” +
” nome TEXT, “+
” cognome TEXT, “+
” telefono INTEGER ” +
“)”;
creatabellaStmt.text=stSQL;
creatabellaStmt.addEventListener(SQLEvent.RESULT,scrivi);
creatabellaStmt.addEventListener(SQLErrorEvent.ERROR,errore);
creatabellaStmt.execute();
}

La prima istruzione passa alla proprietà creatabellaStmt un nuovo oggetto SQLStatement. La seconda istruzione passa alla proprietà sqlConnection dell’oggetto SQLStatement l’istanza connect. Con questi due semplici passaggi creiamo una oggetto per dare ordini SQL e specifichiamo che farà riferimento al  database DBesempio.db.
Alla stringa sql passiamo i comandi destinati a manipolare il database. Osservando il codice è possibile intuire le istruzioni SQL a colpo d’occhio. In pratica, viene creata (se non esiste già) una tabella chiamata rubrica. Allo stesso tempo vengono anche definite la varie colonne della tabella: una chiave primaria, due stringhe (nome e cognome) e un numero intero (il numero di telefono).
Per attribuire l’istruzione SQL al database è necessario passare la stringa sql alla proprietà text dell’oggetto SQLStatement.
Nelle ultime righe della funzione, abbiamo due istruzioni  addEventListener() e un metodo execute(). La prima istruzione  addEventListener() chiama una funzione da eseguire al verificarsi dell’evento RESULT che stabilisce se il comando SQL viene eseguito con successo.
La seconda istruzione  addEventListener() innesca la funzione errore() al verificarsi di un evento ERROR. Infine l’istruzione execute() innesca l’esecuzione del codice SQL.
A ben guardare abbiamo una sequenza di operazioni che potrebbero essere riassunte in uno schema generale valido per tutte le volte in cui vogliamo interagire con un database. Riepilogando:

  1. Si crea un oggetto SQLStatement.
  2. Si passa alla proprietà  sqlConnection dell’oggetto SQLStatement, un’istanza SQLConnection. Questo implica che dobbiamo aver precedentemente creato una connessione con il database tramite la classe SQLConnection (ne abbiamo parlato nell’articolo precedente).
  3. Si passa ad una variabile le istruzioni SQL sottoforma di stringa.
  4. La variabile stringa viene a sua volta passata alla proprietà text dell’oggetto SQLStatement.
  5. Si specificano la funzioni listener da eseguire nel caso in cui le istruzioni SQL vengano eseguite correttamente (evento RESULT).
  6. Si specfica una funzione listener da eseguire in caso di errore.
  7. Si attiva concretamente l’istruzione SQL tramite il metodo execute().

Troviamo questo stesso schema ripetuto in molte istruzioni della nostra classe documento. Per cui, una volta chiarito questo meccanismo, possiamo concentraci sulla logica generale del codice senza ripetere ogni volta gli stessi concetti.
Eravamo rimasti alla creazione della tabella. Un volta creata con successo la tabella, viene chiamata la funzione scrivi(), che si limita a inserire un messaggio nel campo TLF miocampo e chiama la funzione mostraDatiScritti(). Riepiloghiamo il codice della funzione mostraDatiScritti():

private function mostraDatiScritti(e:SQLEvent):void{
campoNome.text=campoCognome.text=campoTelefono.text=””;
selezionaStmt = new SQLStatement();
selezionaStmt.sqlConnection = connect;
var stSQL:String = “SELECT mioid, nome, cognome, telefono FROM rubrica ORDER BY cognome ASC”;
selezionaStmt.text = stSQL;
selezionaStmt.addEventListener(SQLEvent.RESULT,passaDataGrid);
selezionaStmt.addEventListener(SQLErrorEvent.ERROR, errore);
selezionaStmt.execute();
}

In questo caso, invece di creare una tabella, le istruzioni SQL selezionano i dati della tabella in ordine ascendente. Una volta eseguita con successo l’istruzione (al verificarsi dell’evento SQLEvent.RESULT) viene chiamata la funzione listener passaDataGrid() che ha il compito di inserire nel componente DataGrid i dati estrapolati dal database. Vediamo il codice di questa funzione:

private function passaDataGrid(e:SQLEvent):void{
var res:SQLResult=selezionaStmt.getResult();
mioArray=res.data;
var dp:DataProvider = new DataProvider(mioArray);
grid.dataProvider = dp;
grid.columns = [“nome”, “cognome”, “telefono”];
}

Prima di tutto viene creato un’istanza della classe SQLResult. Si tratta di una classe attraverso la quale è possibile accedere ai dati restituiti da un’istruzione SQL tramite il metodo getResult().
Nella riga di codice successiva vengono passati all’array mioArray i dati estrapolati dal database.
Inserendo mioArray come parametro della funzione costruttore della classe DataProvider, otteniamo un oggetto dp da passare alla proprietà dataProvider dell’oggetto grid. Con questa istruzione inseriamo i dati estrapolati nel componente datagrid.
L’ultima istruzione della funzione ci consente di definire in quale ordine visualizzare i dati nel componente. Nel nostro caso avremo prima la colonna dei nomi, poi quella de cognomi e infine la colonne dei numeri di telefono.
Dopo aver analizzato le istruzioni eseguite all’avvio dell’applicazione, passiamo alle funzioni innescate dai pulsanti p1, p2 e p3. Cominciamo da scriviTabella():

private function scriviTabella(e:MouseEvent):void{
if(campoNome.text!=””||campoCognome.text!=””||campoTelefono.text!=””){
inserisciStmt=new SQLStatement();
inserisciStmt.sqlConnection=connect;
var stSQL:String = “INSERT INTO rubrica (nome, cognome, telefono) “;
stSQL += “VALUES (@nome, @cognome, @telefono)”;
inserisciStmt.parameters[“@nome”]=campoNome.text;
inserisciStmt.parameters[“@cognome”]=campoCognome.text;
inserisciStmt.parameters[“@telefono”]=Number(campoTelefono.text);
inserisciStmt.text = stSQL;
inserisciStmt.addEventListener(SQLEvent.RESULT,mostraDatiScritti);
inserisciStmt.addEventListener(SQLErrorEvent.ERROR,errore);
inserisciStmt.execute();
}
}

Rispetto alle altre funzioni dedicate alla manipolazione del database, abbiamo un codice nidificato in un’istruzione condizionale. In altri termini, viene fatto in modo che l’utente possa inserire i dati solo quando i campi non sono vuoti. Per quanto riguarda l’istruzione SQL, in questo caso viene inserito un nuovo contatto della rubrica dove il nome, il cognome e il numero di telefono vengono attribuiti in base al contenuto dei relativi campi TLF. Una volta eseguita correttamente l’istruzione SQL, viene chiamata nuovamente l’istruzione mostraDatiScritti() in modo da visualizzare la rubrica subito dopo l’inserimento dei nuovi dati.
A questo punto passiamo alla funzione cancellaRiga():

private function cancellaRiga(e:MouseEvent):void{
for(var n:int=0;n<mioArray.length;n++){
if(grid.isItemSelected(grid.getItemAt(n))){
cancellaStmt=new SQLStatement();
cancellaStmt.sqlConnection = connect;
var stSQL:String=”DELETE FROM rubrica WHERE mioid = ?”;
cancellaStmt.text=stSQL;
cancellaStmt.parameters[0]=grid.selectedItem.mioid;
cancellaStmt.addEventListener(SQLEvent.RESULT, mostraDatiScritti);
cancellaStmt.addEventListener(SQLErrorEvent.ERROR,errore);
cancellaStmt.execute();
}
}
}

Il ciclo for ci consente di analizzare tutte le righe presenti nell’array alla ricerca di una condizione da verificare. Se uno dei contatti mostrati nel componente è stato selezionato dall’utente, in quel caso vengono eseguite le istruzioni specificate nel codice dell’if().
L’istruzione SQL cancella una riga a partire dal un valore mioid specificato. Tale valore è fornito dalla selezione effettuata sul componente, ovvero dall’istruzione grid.selectedItem.mioid.
Una volta cancellato il record, viene innescata come di consjueto la funzione listener mostraDatiScritti() in modo da visualizzare immediatamente la rubrica con i contatti aggiornati.
Per finire non ci resta che analizzare la funzione cancellaTabella():

private function cancellaTabella(e:MouseEvent):void{
cancellaStmt=new SQLStatement();
cancellaStmt.sqlConnection=connect;
var stSQL:String=”DROP TABLE IF EXISTS rubrica”;
cancellaStmt.addEventListener(SQLErrorEvent.ERROR,errore);
cancellaStmt.text=stSQL;
cancellaStmt.execute();
mioArray=[];
var dpVuoto:DataProvider=new DataProvider(mioArray);
grid.columns=[“nome”, “cognome”, “telefono”];
grid.dataProvider=dpVuoto;
miocampo.appendText(“> La rubrica è stata cancellata…”+”\n”);
creaTabella();
}

L’istruzione SQL passata alla variabile stSQL cancella la tabella rubrica se esiste. subito dopo il metodo execute(), oltre al solito meccanismo usato per dare dei comandi al database, abbiamo una serie di istruzioni “preparatorie” che servono a rendere l’applicazione in grado di proseguire il lavoro nonostante la cancellazione della tabella. In altre parole, l’utente deve essere in grado di poter aggiungere nuovi dati senza incappare in malfunzionamenti. Per cui abbiamo lo svuotamento dell’array, la creazione di un nuovo oggetto DataProvider, un scritta che comunica la cancellazione della tabella e la chiamata al metodo creaTabella() per creare una nuova tabella vuota.

Note finali

Per quanto riguarda la pubblicazione, vale quanto detto la volta scorsa. Inoltre, se non abbiamo cambiato i valori presenti nella finestra Impostazioni AIR, non è necessario disinstallare la versione precedente della rubrica: nel momento in cui andiamo ad installare il file.air, se nome e versione non cambiano, il programma di installazione si limita a sovrascrivere la versione precedente.
Per concludere, una rubrica telefonica in AIR come quella che abbiamo appena descritto ha una qualche utilità pratica? La risposta è no. Usare una rubrica di questo tipo non ha alcuna valenza nel mondo reale.
Però spero che questo esercizio sia stato utile. Nel mio caso, questo stesso script mi ha aiutato a salvare il punteggio di un videogame per Android che sto sviluppando. Della serie: non tutte le rubriche vengono per nuocere… 

Share:

4 comments

    • bruno doper 13 Novembre, 2012 at 10:07 Reply

      A dire il vero non mi sono mai cimentato con la realizzazione un gestionale. Ma credo che AIR abbia tutte le carte in regola.

  1. Luigi 24 Aprile, 2014 at 21:24 Reply

    Ciao Bruno, interessantissimo articolo, ultimamente mi sto cimentando nella creazione di applicazioni mobile (android), ma sto avendo un po di difficoltà, mi chiedevo se potresti farmi un esempio
    di una semplice “select” e “insert”.

    Trovo tutte guide “datate” quindi non so mai se sto facendo bene o male!!

    Da debug credo di essere riuscito a “aprire” il mio DB ma non riesco ne a fare delle semplici INSERT ne SELEC, non riesco a tirare fuori un semplice valore!

    Se hai qualche dritta ti ringrazio!
    ciao
    Luigi

Rispondi a bruno doper Annulla risposta

*