Couchbase Lite, viste e query: un esempio

Home  >>  Sviluppo App  >>  Couchbase Lite, viste e query: un esempio

Couchbase Lite, viste e query: un esempio

On maggio 4, 2015, Posted by , In Sviluppo App, By , , With No Comments

Introduzione

Questo articolo fa seguito al precedente “Couchbase Lite: utilizzo delle viste”, e mostra i passi necessari per la realizzazione di una semplice app che, dopo aver memorizzato una serie di dati nel database, li visualizza in una ListView ricavandoli dall’interrogazione ad un vista.

Per la configurazione del progetto Android Studio e l’uso di Genymotion per gli emulatori, si rimanda all’articolo: “Esempio pratico di utilizzo di Couchbase Lite”.

Struttura del progetto

L’immagine seguente mostra la struttura del progetto. Come si vede vengono utilizzate due classi Java, una  per la definizione dell’Activity principale, l’altra per la creazione del database, e due layout, che vedremo nel seguito.

progetto

Struttura del progetto Android Studio

DataBaseManager

La classe si occupa solamente di fornire un riferimento al database il cui nome è indicato dall’utente nella EditText presente nella prima riga dello screen. Come si può notare non viene applicato alcun pattern particolare (ad es. un Singleton), si tratta di una semplice classe Java.



public class DataBaseManager {
    //top level database manager
    private Manager manager;
    //database in cui memorizzare gli oggetti
    private Database database;
    //AndroidContext
    AndroidContext ctx;
    public DataBaseManager(AndroidContext ctx) {
        super();
        this.ctx = ctx;
    }
    private final String TAG = "DataBaseManager";
    public Database getDatabase(String dbName) {
        try {
//bisogna passare il contesto applicativo della MainActivity settato nel costruttore      
            manager = new Manager(this.ctx, Manager.DEFAULT_OPTIONS);       
        } catch (IOException e) {
            android.util.Log.e(TAG, "ERRORE NELLA CREAZIONE DEL MANAGER.");
        }
        android.util.Log.w(TAG, "------# Verifico se il nome del database e' valido");
        if (!Manager.isValidDatabaseName(dbName)) {
            android.util.Log.w(TAG, "------# Il nome del database e' valido");
        }
        try {
            android.util.Log.w(TAG, "------# Creo il database");
            database = manager.getDatabase(dbName);      
            android.util.Log.w(TAG, "------# Database creato");
        } catch (CouchbaseLiteException e) {
            android.util.Log.e(TAG, "------# ERRORE NELLA CREAZIONE DEL DATABASE");
        }
        return database;
    }
}

Il metodo getDatabase(String dbName) ottiene dalla MainActivity il nome del database da creare e lo controlla. Per motivi di portabilità in Couchbase Lite il nome del db non deve contenere lettere maiuscole.

Creazione del database

Utilizzando un’istanza della classe DataBaseManager la creazione del database è molto semplice; abbiamo anche aggiunto un ChangeListener della classe Database per evidenziare che ogni modifica al database può essere gestita, per esempio con una notifica. In questo caso viene semplicemente creato un Toast ogni volta che un nuovo documento è inserito:

private void creaDB(String nomeDB) {
    manager = new DataBaseManager(new AndroidContext(this));
    database = manager.getDatabase(nomeDB);
    if (database != null) {
        database.addChangeListener(new Database.ChangeListener() {
            public void changed(Database.ChangeEvent event) {
                Context context = getApplicationContext();
                CharSequence text = "Aggiunto un record: " + database.getDocumentCount();
                int duration = Toast.LENGTH_SHORT;
                Toast toast = Toast.makeText(context, text, duration);
                toast.show();
            }
        });
    }
}

La activity principale

La MainActivity semplicemente mostra tre componenti:

  • Un EditText in cui l’utente deve inserire il nome del database da creare.
  • Un Button: cliccando su di esso viene ricavato il nome del database da creare e, se non è vuoto, vengono caricati i dati, creata la view e interrogata per ottenere i risultati.
  • Un ListView che mostrerà le righe lette e restituite dalla view.

I layout

Sono presenti due layout: uno per la main activity e l’altro per configurare la ListView mediante il metodo:

listAdapter = new ArrayAdapter(this, R.layout.simplerow);

activity_main.xml:


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent" android:weightSum="1">
<EditText
android:id="@+id/editText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="Inserisci il nome del database..."/>
<Button
android:id="@+id/bottone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="crea db"/>
<ListView android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="@+id/mainListView">
    </ListView>

</LinearLayout>


simplerow.xml:

<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/rowTextView"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="10dp"
android:textSize="16sp" >
</TextView>


 

Inserimento dei dati nel database

 

 Supponiamo di voler gestire una lista di utenti in documenti JSON individuabili tramite la chiave “tipo” con valore “LDAP”. Nella nostra semplice applicazione l’unica funzionalità implementata, oltre alla creazione del database, è quella di visualizzare la lista di dati inseriti nel database. In questo caso il docID viene creato utilizzando un oggetto di classe UUID, il cui valore viene anche assegnato alla chiave “ID” del documento.


 private void loadData() {
        Log.w(TAG, "------# carico i documenti nel database");
String[][] utenti = {{"LDAP", "Pier Luigi", "Miglioli", "pmiglioli@f2informatica.it", "Italia", "Lombardia", "Cremona", "3397393859", "MGLPLG57D14D150V"},
{"LDAP", "Massimo", "Ferracci", "mferracci@f2informatica.it", "Italia", "Lombardia", "Milano", "3283456789", "FRRMSM64D14F205D"},
{"LDAP", "Mario", "Rossi", "mrossi@f2informatica.it", "Italia", "Lombardia", "Bergamo", "3387393859", "RSSMRO45T13T404K"},
{"LDAP", "Marco", "Bianchi", "cbianchi@gmail.com", "Italia", "Lombardia", "Varese", "3287568493", "BNCMRC34G15H723T"},
{"LDAP", "Alessandro", "Manzoni", "a.manzoni@hotmail.it", "Italia", "Lombardia", "Brescia", "3398131258", "CLNGNN21E32J345H"},
{"LDAP", "Dante", "Alighieri", "dalighieri@gmail.com", "Italia", "Lombardia", "varese", "3394343333", "BNDUBR11F22K122J"},
{"LDAP", "Ugo", "Foscolo", "ufo@hotmail.com", "Italia", "Lombardia", "Pavia", "3334567654", "DRNDGO01G23L199L"},
{"LDAP", "Patrick", "O'Brian", "pob@f2informatica.it", "Italia", "Lombardia", "Mantova", "0372459999", "MNCETR02L22P212T"},
};
        int size = utenti.length;
        for (int i = 0; i < size; i++) {
            Document document = database.createDocument();
            // creazione di un oggetto che contiene i dati da memorizzare
Map<String, Object> docContent = new HashMap<String, Object>();
            docContent.put("ID", String.valueOf(UUID.randomUUID()));
            docContent.put("tipo", utenti[i][0]);
            docContent.put("nome", utenti[i][1]);
            docContent.put("cognome", utenti[i][2]);
            docContent.put("email", utenti[i][3]);
            docContent.put("nazione", utenti[i][4]);
            docContent.put("regione", utenti[i][5]);
            docContent.put("citta", utenti[i][6]);
            docContent.put("telefono", utenti[i][7]);
            docContent.put("codiceFiscale", utenti[i][8]);
// aggiungo i dati al documento
try {
                document.putProperties(docContent);
            } catch (CouchbaseLiteException e) {
                Log.e(TAG, "ERRORE NEL CARICAMENTO DEI DATI");
            }
        }
    }

Creazione della view

La view ritornerà i dati nome, cognome, telefono e codice fiscale che, nel loro insieme costituiscono l’indice; il metodo database.getView() crea la nuova view se non esiste, altrimenti restituisce la view già esistente.



private void createView() {
        Log.w(TAG, "------# creo la View");
        // Crea la view e registra la map function:
        View usersView = database.getView("users");
        usersView.setMap(new Mapper() {
            @Override
            public void map(<String, Object> document, Emitter emitter) {
                if (document.get("tipo").equals("LDAP")) {
                    String associatedID = (String) document.get("ID");
                    List<object> key = new ArrayList<Object>(); 
                   //la view ritornera' i dati [nome, cognome, telefono, codice fiscale]
                    key.add(document.get("nome"));
                    key.add(document.get("cognome"));
                    key.add(document.get("telefono"));
                    key.add(document.get("codiceFiscale"));
                    HashMap<String, Object> value = new HashMap<String, Object>();
                    value.put("_id", associatedID);
                    emitter.emit(key, value);
                }
            }
        }, "1");
    }

Interrogazione della view

Il metodo listData() restituisce un ArrayList di Object che verrà utilizzato dall’ArrayAdapter associato alla ListView:

private ArrayList<Object> listData() {
    ArrayList<Object> listaDati = new ArrayList<Object>();
    View usersView = database.getView("users");
    Query query = usersView.createQuery();
    query.setLimit(100);
    try {
        QueryEnumerator result = query.run();

        for (Iterator<QueryRow> it = result; it.hasNext(); ) {
            QueryRow row = it.next();
            //il metodo row.getKey() ritorna un oggetto
listaDati.add(row.getKey());

        }
    } catch (CouchbaseLiteException ex) {
        Log.e(TAG, "Errore nell'esecuzione della query");
    }
    return listaDati;
}

 

Il metodo resituisce un ArrayList di oggetti che serviranno per caricare l’Adapter con cui viene viene popolata la ListView.

 

Popolamento dell’Adapter

Il metodo descritto sopra viene invocato sull’OnClickListener() associato al bottone button nel seguente modo:


@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button button = (Button) findViewById(R.id.bottone);
        final EditText editText = (EditText) findViewById(R.id.editText);
        final ListView mainListView = (ListView) findViewById(R.id.mainListView);
        listAdapter = new ArrayAdapter(this, R.layout.simplerow);
              button.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(android.view.View v) {
                dbName=editText.getText().toString();
                if(dbName!= null && !dbName.equalsIgnoreCase("")) {
                    creaDB(dbName.toLowerCase());
                    loadData();
                    createView();
                    listAdapter.addAll(listData());
                    // Setta l'ArrayAdapter alla listView
                    mainListView.setAdapter(listAdapter);
                }else{
                    Context context = getApplicationContext();
                    CharSequence text = "Il nome del DB non puo' essere vuoto";
                    int duration = Toast.LENGTH_SHORT;
                    Toast toast = Toast.makeText(context, text, duration);
                    toast.show();
                    return;
                }

            }
        });
// Crea l'Adapter
        if(dbName != null && !dbName.equalsIgnoreCase("")) {
            listAdapter = new ArrayAdapter(this, R.layout.simplerow, listData());
            // Setta l'ArrayAdapter alla listView
            mainListView.setAdapter(listAdapter);
        }

    }


Risultato finale

cblite

L’app dopo il recupero dei dati dal db

Conclusioni

Utilizzando Couchbase Lite la memorizzazione di documenti in formato JSON, quindi molto leggeri e flessibili, è particolarmente semplice. A differenza di SQLite, non è necessario definire Query SQL particolari, né in fase di creazione del DB, né in fase di interrogazione. Il footprint dell’app è molto piccolo. Inoltre, come vedremo in un successivo articolo, mediante questa tecnologia è possibile sincronizzare i dati presenti sul dispositivo con un cluster di server Couchbase remoti usando il Sync Gateway e sfruttare quindi la scalabilità e affidabilità fornite dai server Couchbase.

Lascia un Commento