JavascriptProva

Visualizzazione post con etichetta Firebase. Mostra tutti i post
Visualizzazione post con etichetta Firebase. Mostra tutti i post

mercoledì 26 ottobre 2016

Codici per cancellare un record e per aggiungerlo o modificarlo, basati sulla chiave primaria.

Bene.
Finora mi sono occupato del cancellare di sana pianta tutto il database Firebase e riscriverlo da cellulare, e viceversa.

Ora mi dovrei occupare del modificare singoli record, perché se si modifica un singolo record cancellare e riscrivere tutto il database mi sembra un po' come quello che si compra la macchina nuova perché i posacenere di quella vecchia sono ormai pieni.

Innanzitutto devo cercare sul Firebase il mio record mediante una chiave, e userò la chiave primaria _id.
La ricerca andrà fatta mediante le query di firebase.
Se la ricerca non è vana, ossia se la query dà dei risultati, posso cancellare o modificare il mio singolo record mediante l'apposito codice.
Se ho da aggiungere un record, invece, nel caso la query mi dia ricerca vana devo usare il push().

Posso unire le due cose dicendo "Se c'è modificalo, se non c'è aggiungilo".
Bene: primo compito. Individuare un record e cancellarlo.

Per individuare un record devo creare una query.
Mettiamo che voglio individuare il record n.2
Query query=reference.orderByChild("_id").equalTo(2);
Ora pongo la condizione che esista. Vediamo il numero di children che ha la query:
                Query query=reference.orderByChild("_id").equalTo(2);
                query.addListenerForSingleValueEvent(new ValueEventListener() {
                    @Override
                    public void onDataChange(DataSnapshot dataSnapshot) {
                        System.out.println(dataSnapshot.getChildrenCount());
                    }

                    @Override
                    public void onCancelled(DatabaseError databaseError) {

                    }
                });
Ci sono riuscito.
Funziona egregiamente.

Ora creo la funzione "cancella" per cancellare selettivamente questo record. Ecco:
                Query query=reference.orderByChild("_id").equalTo(2);
                query.addListenerForSingleValueEvent(new ValueEventListener() {
                    @Override
                    public void onDataChange(DataSnapshot dataSnapshot) {
                        dataSnapshot.getRef().removeValue();
                    }

                    @Override
                    public void onCancelled(DatabaseError databaseError) {

                    }
                });
Vediamo...

No, non funziona. Il dataSnapshot.getRef() così facendo è ancora relativo alla radice del database.
Provvedo:
                Query query=reference.orderByChild("_id").equalTo(2);
                query.addListenerForSingleValueEvent(new ValueEventListener() {
                    @Override
                    public void onDataChange(DataSnapshot dataSnapshot) {
                        for(DataSnapshot child : dataSnapshot.getChildren()){
                            child.getRef().removeValue();
                        }

                    }

                    @Override
                    public void onCancelled(DatabaseError databaseError) {

                    }
                });
E così funziona.

Ora devo provvedere al codice che aggiunge un record al database nel caso questo sia assente.
        bttAggiungiRecord.setOnClickListener(new View.OnClickListener(){

            @Override
            public void onClick(View v) {
                Query query=reference.orderByChild("_id").equalTo(2);

                query.addListenerForSingleValueEvent(new ValueEventListener() {
                    @Override
                    public void onDataChange(DataSnapshot dataSnapshot) {
                        if(dataSnapshot.getChildrenCount()==0){
                            Cursor crs=helper.query();
                            crs.moveToFirst();
                            do{
                                if(crs.getInt(0)==2){
                                    PersonaOut p=new PersonaOut(crs.getInt(0),
                                            crs.getString(1),crs.getString(2),
                                            crs.getInt(3),crs.getString(4));
                                    reference.push().setValue(p);
                                }
                            }while(crs.moveToNext());

                        }
                    }

                    @Override
                    public void onCancelled(DatabaseError databaseError) {

                    }
                });
            }
        });
Benissimo!
Ora voglio fare che invece se è presente lo modifica.
Ecco, sì:
        bttAggiungiRecord.setOnClickListener(new View.OnClickListener(){

            @Override
            public void onClick(View v) {

                Cursor crs=helper.query();
                crs.moveToFirst();
                do{
                    if(crs.getInt(0)==2){
                        p=new PersonaOut(crs.getInt(0),
                                crs.getString(1),crs.getString(2),
                                crs.getInt(3),crs.getString(4));
                    }
                }while(crs.moveToNext());

                Query query=reference.orderByChild("_id").equalTo(2);
                query.addListenerForSingleValueEvent(new ValueEventListener() {
                    @Override
                    public void onDataChange(DataSnapshot dataSnapshot) {
                        if(dataSnapshot.getChildrenCount()==0){
                            System.out.println(dataSnapshot.getChildrenCount());
                            reference.push().setValue(p);
                        }
                        if(dataSnapshot.getChildrenCount()==1){
                            System.out.println(dataSnapshot.getChildrenCount());
                            for(DataSnapshot child : dataSnapshot.getChildren()){
                                child.getRef().setValue(p);
                            }
                        }
                    }

                    @Override
                    public void onCancelled(DatabaseError databaseError) {

                    }
                });
            }
        });

martedì 25 ottobre 2016

Esercizi con Firebase e Query

Cerchiamo di mettere a fuoco i problemi di questo database Firebase.
Perché devo ottenere uno snapshot per posizionarmi su un record?
Perché devo posizionarmi su un record?

Perché devo agire su di essi uno per uno. Per dire "cancella un record" devo specificare il record stesso.
Proviamo a cancellare un solo record

reference= FirebaseDatabase.getInstance().getReference();
...
reference.child("-KUw45Hi6pkgGcRzwa7h").removeValue();
e vediamo se funziona.

Certo che funziona! Ma il problema fondamentale è uno solo: io non conosco i nomi dei records, in quanto vengono assegnati dal sistema, e non posso conoscerli in anticipo (oltre al fatto che sono difficili da ricordare).
Dunque devo ricavarli.

Ammettiamo che io debba fare la stessa operazione senza conoscere il nome del record. Come faccio?
L'unica è ottenere uno snapshot del database stesso.
Ci provo, a ottenere un dataSnapshot. Ecco, così ottengo un dataSnapshot che figura fra i parametri.
Un dataSnapshot ha la proprietà getKey() che dovrebbe restituirmi il nome del nodo.
Ma se io faccio così su un dataSnapshot ottenuto dalla radice del database...
                reference.addListenerForSingleValueEvent(new ValueEventListener() {
                    @Override
                    public void onDataChange(DataSnapshot dataSnapshot) {
                        System.out.println(dataSnapshot.getKey());
                    }

                    @Override
                    public void onCancelled(DatabaseError databaseError) {

                    }
                });
...ottengo null.
Però posso ottenere anche i dataSnapshot degli elementi "discendenti" della radice, ossia di ogni figlio di essa, vale a dire di ogni nodo, perché il dataSnapshot ha la proprietà getChildren(), che mi ottiene i dataSnapshot anche degli elementi figli.
                reference.addListenerForSingleValueEvent(new ValueEventListener() {
                    @Override
                    public void onDataChange(DataSnapshot dataSnapshot) {
                        for(DataSnapshot child : dataSnapshot.getChildren()){
                            System.out.println(child.getKey());
                        }
                    }

                    @Override
                    public void onCancelled(DatabaseError databaseError) {

                    }
                });
Con questa operazione ottengo i datasnapshot di tutti i figli.
Vediamo se mi dà i nomi "illeggibili".
10-25 16:42:43.585 7992-7992/? I/System.out: -KUwDcEfCy2-QPeLkNFS
10-25 16:42:43.585 7992-7992/? I/System.out: -KUwDcEnBfijEAoDzfkW
10-25 16:42:43.585 7992-7992/? I/System.out: -KUwDcEoAzah1_AhLlxC
Perfetto!
Dunque per cancellare il primo record, facendo così, dobbiamo ottenere il nome del nodo in questo modo.
Però esso viene ottenuto insieme a tutti gli altri... Come fare per isolare quello che ci interessa?

Provo a fare così:
                reference.addListenerForSingleValueEvent(new ValueEventListener() {
                    @Override
                    public void onDataChange(DataSnapshot dataSnapshot) {
                        List<String> lista=new ArrayList<String>();
                        for(DataSnapshot child : dataSnapshot.getChildren()){
                            System.out.println(child.getKey());
                            lista.add(child.getKey());
                            
                        }
                        reference.child(lista.get(0)).removeValue();
                    }

                    @Override
                    public void onCancelled(DatabaseError databaseError) {

                    }
                });
ossia metto i risultati in un'ArrayList e individuo il primo immesso come quello di indice zero.
Vediamo se funziona...

Sì! Funziona!
Ma posso anche usare direttamente il riferimento a un nodo figlio?
                    public void onDataChange(DataSnapshot dataSnapshot) {
                        List<DatabaseReference> lista=new ArrayList<DatabaseReference>();
                        for(DataSnapshot child : dataSnapshot.getChildren()){
                            System.out.println(child.getKey());
                            lista.add(child.getRef());

                        }
                        lista.get(0).removeValue();
                    }
Sì, anche così funziona.
Ma posso usare anche un dataSnapshot di cui prendere il riferimento poi:
                reference.addListenerForSingleValueEvent(new ValueEventListener() {
                    @Override
                    public void onDataChange(DataSnapshot dataSnapshot) {
                        List<DataSnapshot> lista=new ArrayList<DataSnapshot>();
                        for(DataSnapshot child : dataSnapshot.getChildren()){
                            System.out.println(child.getKey());
                            lista.add(child);

                        }
                        lista.get(0).getRef().removeValue();
                    }
Quindi, alla fine il removeValue() si applica a un riferimento.
Questo riferimento si può ottenere ottenendo il DataSnapshot da cui ottenere il riferimento mediante getRef(), subito o successivamente, o ottenendo, sempre dal DataSnapshot il Value da cui poi ottenere il riferimento mediante il metodo .child() del riferimento della radice.


Ora, anziché andare per numero di ordine, volendo eliminare il primo (o anche il secondo eccetera) dei records, voglio eliminare solo quello che presenti determinate caratteristiche.
Devo costruirmi una query.

Provo...
Query query=reference.orderByChild("cognome").equalTo("Rossi");
Sono un po' perplesso perché il child di reference è quello identificato dal nome irripetibile... ma forse il "child" di questo caso si riferisce anche ai "nipoti"...
Comunque questa perplessità servirà a farmi ricordare meglio la cosa.
Ora praticamente a una query io posso applicare un ValueEventListener?

Sembrerebbe di sì, e questo significa che ne posso ricavare un dataSnapshot e quindi un insieme di Children...
Vediamo:
        bttRefreshFB.setOnClickListener(new View.OnClickListener(){

            @Override
            public void onClick(View v) {

                reference.addListenerForSingleValueEvent(new ValueEventListener() {
                    @Override
                    public void onDataChange(DataSnapshot dataSnapshot) {

                        Query query=reference.orderByChild("cognome").equalTo("Rossi");
                        query.addListenerForSingleValueEvent(new ValueEventListener() {
                            @Override
                            public void onDataChange(DataSnapshot dataSnapshot) {
                                System.out.println(dataSnapshot.getChildrenCount());
                            }

                            @Override
                            public void onCancelled(DatabaseError databaseError) {

                            }
                        });
                    }

                    @Override
                    public void onCancelled(DatabaseError databaseError) {

                    }
                });





            }



        });
Ecco: partendo dalla query ho ottenuto un dataSnapshot contenente tutti quei record che hanno un campo "cognome" uguale a "Rossi".
Ottengo, come mi aspettavo un numero di children pari a 1 in quanto solo un record è quello in cui "cognome" è uguale a "Rossi".

Bene, quindi dato che con la query posso selezionare, adesso voglio eliminare i records (ce n'è uno solo) in cui "cognome" equivale a "Minchia".
                reference.addListenerForSingleValueEvent(new ValueEventListener() {
                    @Override
                    public void onDataChange(DataSnapshot dataSnapshot) {

                        Query query=reference.orderByChild("cognome").equalTo("Minchia");
                        query.getRef().removeValue();

                    }

                    @Override
                    public void onCancelled(DatabaseError databaseError) {

                    }
                });
E invece così facendo ho eliminato tutti i records!
Perché?
Forse perché devo prima ottenere un dataSnapshot.
Ma è proprio indispensabile che ottenga la query nell'amito del metodo addListenerForSingleValueEvent?
No. Query agisce direttamente su reference, e mi seleziona i figli in relazione al criterio proposto.
Riscrivo:
        bttRefreshFB.setOnClickListener(new View.OnClickListener(){

            @Override
            public void onClick(View v) {
                Query query=reference.orderByChild("cognome").equalTo("Minchia");
                query.getRef().removeValue();

            }



        });
Provo...

E ottengo lo stesso risultato.
Query è una selezione di un riferimento, quindi va soggetta agli stessi criteri di un riferimento.

        bttRefreshFB.setOnClickListener(new View.OnClickListener(){

            @Override
            public void onClick(View v) {
                Query query=reference.orderByChild("cognome").equalTo("Minchia");
                query.addListenerForSingleValueEvent(new ValueEventListener() {
                    @Override
                    public void onDataChange(DataSnapshot dataSnapshot) {
                        for(DataSnapshot child : dataSnapshot.getChildren()){
                            child.getRef().removeValue();
                        }
                    }

                    @Override
                    public void onCancelled(DatabaseError databaseError) {

                    }
                });

            }



        });


Perfetto!!! Adesso funziona!

Refreshing di tutto il database Firebase

Ripartiamo con i database.
Ho un SQLiteDatabase. Voglio portarlo su Firebase.
        Cursor crs=helper.query();r
        crs.moveToFirst();
        do{
            PersonaOut personaOut=new PersonaOut(crs.getInt(0),crs.getString(1),crs.getString(2),
                    crs.getInt(3),crs.getString(4));
            reference.push().setValue(personaOut);
        }while(crs.moveToNext());
Vediamo se funziona...

Sì, funziona perfettamente.


Ora la situazione è più complessa: vorrei modificare i dati nel database SQLite e trasferire queste modifiche al database online.
Come fare?

Intanto, mi creo un metodo in Helper che consenta di modificare il campo "professione".
    public void update(String cognome,String professione){
        SQLiteDatabase db=this.getWritableDatabase();
        ContentValues values=new ContentValues();
        values.put("PROFESSIONE",professione);
        db.update("TABELLA",values, "PROFESSIONE=?",new String[]{cognome});
    }
E verifico che funzioni.

Funziona! Per ora ho solo i risultati in locale.
Trasferendoli su Firebase con questo
        button.setOnClickListener(new View.OnClickListener(){

            @Override
            public void onClick(View v) {
                Cursor crs=helper.query();
                crs.moveToFirst();
                do{
                    PersonaOut personaOut=new PersonaOut(crs.getInt(0),crs.getString(1),crs.getString(2),
                            crs.getInt(3),crs.getString(4));
                    reference.push().setValue(personaOut);
                }while(crs.moveToNext());
            }
        });
ottengo l'aggiunta dei record a quelli già presenti sul database, con le opportune modifiche.


Ho penato molto per cancellare il database remoto e riscriverlo.
Ho trovato infine la soluzione usando addListenerForSingleValueEvent.
Il problema è che se mettevo un semplice addValueEventListener al reference, ossia al riferimento alla radice del database, una volta settato il listener, la successiva riscrittura di dati, essendo comunque un evento, triggerava il listener, e dato che il codice del listener era quello di cancellare i dati, un attimo dopo questi venivano cancellati.
Usando un listener valido per una sola operazione non ci sono più problemi.
Ora riscrivo il codice per la cancellazione e il refreshing di tutto il database.
        bttRefreshFB.setOnClickListener(new View.OnClickListener(){

            @Override
            public void onClick(View v) {
                reference.addListenerForSingleValueEvent(new ValueEventListener() {

                    @Override
                    public void onDataChange(DataSnapshot dataSnapshot) {
                        for(DataSnapshot figlio : dataSnapshot.getChildren()){
                            DatabaseReference ref= dataSnapshot.getRef();
                            ref.removeValue();

                        }
                        Cursor crs=helper.query();
                        crs.moveToFirst();
                        do{
                            PersonaOut personaOut=new PersonaOut(crs.getInt(0),crs.getString(1),crs.getString(2),
                                    crs.getInt(3),crs.getString(4));
                            reference.push().setValue(personaOut);
                        }while(crs.moveToNext());

                    }

                    @Override
                    public void onCancelled(DatabaseError databaseError) {

                    }
                });


            }



        });
Ecco, il refreshing funziona.

domenica 23 ottobre 2016

Updating di Firebase.

Bene, ho trovato qualcosa sull'updataggio di Firebase.
E' meglio che non ci mettiamo sbarramenti e andiamo fluidi come l'acqua.
Seguo un codice di StackOverflow...

Intanto inizio dal riferimento al database.
reference= FirebaseDatabase.getInstance().getReference();
Ora faccio riferimento a un record (child del nodo principale), usando il nome del nodo, quello "impronunciabile" attribuito dal sistema:
        reference= FirebaseDatabase.getInstance().getReference();

        DatabaseReference ref= reference.child("-KUae5VW3jfCoWP2yQmc");
        ref.addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                System.out.println(dataSnapshot.getValue());
            }

            @Override
            public void onCancelled(DatabaseError databaseError) {

            }
        });
E funziona:
10-23 13:10:57.145 19033-19033/? I/System.out: {cognome=Rossi, professione=Stupratore, nome=Mario, eta=45}
Ora, se io cerco nello specifico il nodo che si chiama Rossi, anziché identificarlo per mezzo del codice impronunciabile del nodo?
Usare una query.
Come si usano le query su Firebase?

Ecco, io voglio cercare Rossi, non Hxydkjlsdknsokdgrunth eccetera: come devo fare?
Ci provo...

Ottengo la reference al database:
reference= FirebaseDatabase.getInstance().getReference();
Ora, nella situazione di prima io selezionavo il nodo con il nome impronunciabile facendo così:
DatabaseReference ref= reference.child("-KUae5VW3jfCoWP2yQmc");
mentre ora provo questo:
        reference= FirebaseDatabase.getInstance().getReference();
        reference.orderByChild("cognome").equalTo("Rossi").addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                System.out.println(dataSnapshot.getValue());
            }

            @Override
            public void onCancelled(DatabaseError databaseError) {

            }
        });
10-23 18:02:42.041 26561-26561/? I/System.out: {-KUae5VW3jfCoWP2yQmc={cognome=Rossi, professione=Stupratore, nome=Mario, eta=45}}
Perfetto! Ora provo a eliminare la condizione equalTo:
         reference.orderByChild("cognome").addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                System.out.println(dataSnapshot.getValue());
            }

            @Override
            public void onCancelled(DatabaseError databaseError) {

            }
        });
10-23 18:13:41.641 28531-28531/? I/System.out: {-KUae5VrVyXMk2lFt39t={cognome=Bianchi, professione=Squartatore, nome=Carlo, eta=54}, -KUae5VW3jfCoWP2yQmc={cognome=Rossi, professione=Stupratore, nome=Mario, eta=45}, -KUae5VsYGesGotl6PZf={cognome=Verdi, professione=Serial killer, nome=Luigi, eta=36}}
Ecco: ottengo tutti.

Ma se faccio OrderByChild("nome"):
        reference.orderByChild("nome").addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                System.out.println(dataSnapshot.getValue());
            }

            @Override
            public void onCancelled(DatabaseError databaseError) {

            }
        });
10-23 18:20:05.071 29020-29020/? I/System.out: {-KUae5VrVyXMk2lFt39t={cognome=Bianchi, professione=Squartatore, nome=Carlo, eta=54}, -KUae5VW3jfCoWP2yQmc={cognome=Rossi, professione=Stupratore, nome=Mario, eta=45}, -KUae5VsYGesGotl6PZf={cognome=Verdi, professione=Serial killer, nome=Luigi, eta=36}}
L'ordine è sempre lo stesso.

Ora voglio selezionare quello che si chiama Luigi di nome:
        reference.orderByChild("nome").equalTo("Luigi").addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                System.out.println(dataSnapshot.getValue());
            }

            @Override
            public void onCancelled(DatabaseError databaseError) {

            }
        });
10-23 18:27:48.931 31604-31604/? I/System.out: {-KUae5VsYGesGotl6PZf={cognome=Verdi, professione=Serial killer, nome=Luigi, eta=36}}
Perfetto!

Adesso voglio trovare quel simpatico galantuomo che fa di professione lo squartatore:
        reference.orderByChild("professione").equalTo("Squartatore").addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                System.out.println(dataSnapshot.getValue());
            }

            @Override
            public void onCancelled(DatabaseError databaseError) {

            }
        });
10-23 18:29:49.581 32582-32582/? I/System.out: {-KUae5VrVyXMk2lFt39t={cognome=Bianchi, professione=Squartatore, nome=Carlo, eta=54}}


Perfetto!
Ora faccio cambiare mestiere a un altro galantuomo per vedere se con la query "Squartatore" me li identifica tutti e due. Ecco, Mario Rossi non fa più lo stupratore ma fa lo squartatore anche lui.
Vediamo...

Non c'è stato bisogno di far correre il programma ancora: dato che il cambiamento del database mi attiva il listener ho ottenuto direttamente sulla finestra LogCat questo:
10-23 18:32:10.361 32582-32582/? I/System.out: {-KUae5VrVyXMk2lFt39t={cognome=Bianchi, professione=Squartatore, nome=Carlo, eta=54}, -KUae5VW3jfCoWP2yQmc={cognome=Rossi, professione=Squartatore, nome=Mario, eta=45}}
Perfetto!
Ora vediamo: esiste un "tipo" Query che identifichi questi gruppi selezionati per mezzo di query?
        Query query= reference.orderByChild("professione").equalTo("Squartatore");
        
        query.addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                System.out.println(dataSnapshot.getValue());
            }

            @Override
            public void onCancelled(DatabaseError databaseError) {

            }
        });
Sì, esiste. Posso immagazzinare alcune queries in modo da avere la selezione già preconfezionata, forse...

Ora Mario Rossi ha messo la testa a posto e ha cambiato vita, e non fa più lo stupratore o lo squartatore, ma adesso fa il chierichetto presso la parrocchia.
Come farlo sapere anche a Firebase?

Vediamo...

Vado sul nodo:
        reference= FirebaseDatabase.getInstance().getReference();
        DatabaseReference ref=reference.child("-KUae5VW3jfCoWP2yQmc");
Quindi creo una HashMap, e ci metto "professione" e "Chierichetto".
Quindi applico al nodo in cui mi trovo il metodo updateChildren().
        Map<String,Object> mappa=new HashMap<String,Object>();
        mappa.put("professione","Chierichetto");
        ref.updateChildren(mappa);
E funziona!



Ma ho identificato il galantuomo in questione con il nome impronunciabile del nodo.
Ora lo identifico in modo più umano.
Faccio una query con il cognome.
Anche Luigi Verdi ha cambiato vita, e adesso non fa più il serial killer ma il sagrestano.
Identifichiamolo con il nome.

Ho avuto molta difficoltà a individuare il nome del nodo dallo snapshot. Ecco:
        Query query= reference.orderByChild("cognome").equalTo("Verdi");

        query.addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {

                for(DataSnapshot child : dataSnapshot.getChildren()){
                    DatabaseReference r=reference.child(child.getKey());
                    Map<String,Object> mappa=new HashMap<String, Object>();
                    mappa.put("professione","sagrestano");
                    r.updateChildren(mappa);
                }

            }

            @Override
            public void onCancelled(DatabaseError databaseError) {

            }
        });
E finalmente Verdi fa il sagrestano!



Anche l'altro galantuomo, Carlo Bianchi, rispettabile squartatore, ha deciso di mettere la testa a posto, e si fa addirittura frate! Dunque cambia anche nome: prenderà il nome di Fra' Mamozzio.
Vediamo se riusciamo a farlo sapere a Firebase...
        Query query= reference.orderByChild("cognome").equalTo("Bianchi");

        query.addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                for(DataSnapshot record : dataSnapshot.getChildren()){
                    DatabaseReference r = reference.child(record.getKey());
                    Map<String,Object> mappa = new HashMap<String, Object>();
                    mappa.put("professione","frate");
                    mappa.put("nome","Fra Mamozzio");
                    r.updateChildren(mappa);
                    

                }


            }

            @Override
            public void onCancelled(DatabaseError databaseError) {

            }
        });
Questa volta abbiamo corretto due campi. Vediamo se funziona...



Perfetto!!!

Pensieri sparsi sulla struttura di JSON

E' necessario che giri un po' intorno a questo database Firebase...

Ho usato la sintassi con getValue() direttamente in una classe (che dovrebbe essere denominata POJO, in questo caso)...
Riprovo a ricostruire il codice. Con questo, io prendo riferimento al primo nodo del database:
reference= FirebaseDatabase.getInstance().getReference();
Ora mi esploro questo nodo di base.
        reference= FirebaseDatabase.getInstance().getReference();

        reference.addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                System.out.println(dataSnapshot.getValue());
            }

            @Override
            public void onCancelled(DatabaseError databaseError) {

            }
        });
10-22 13:32:17.125 3352-3352/? I/System.out: {-KUae5VrVyXMk2lFt39t={cognome=Bianchi, professione=Squartatore, nome=Carlo, eta=54}, -KUae5VW3jfCoWP2yQmc={cognome=Rossi, professione=Stupratore, nome=Mario, eta=45}, -KUae5VsYGesGotl6PZf={cognome=Verdi, professione=Serial killer, nome=Luigi, eta=36}}
Ecco, ho ottenuto tutto il mio piccolo database di soggetti infami recuperando il nodo radice.
Vediamo meglio come sta messo...
{
-KUae5VrVyXMk2lFt39t={cognome=Bianchi, professione=Squartatore, nome=Carlo, eta=54}, 
-KUae5VW3jfCoWP2yQmc={cognome=Rossi, professione=Stupratore, nome=Mario, eta=45}, 
-KUae5VsYGesGotl6PZf={cognome=Verdi, professione=Serial killer, nome=Luigi, eta=36}
}
E andiamoci a studiare la struttura del JSON.

Confrontiamo con un esempio preso da un tutorial:
var JSON = {
  proprieta1: 'Valore',
  proprieta2: 'Valore',
  proprietaN: 'Valore'
}
e mi coloro quell'altro in modo analogo:
reference= {
-KUae5VrVyXMk2lFt39t={cognome=Bianchi, professione=Squartatore, nome=Carlo, eta=54}, 
-KUae5VW3jfCoWP2yQmc={cognome=Rossi, professione=Stupratore, nome=Mario, eta=45}, 
-KUae5VsYGesGotl6PZf={cognome=Verdi, professione=Serial killer, nome=Luigi, eta=36}
}
Ecco, ogni nodo contiene un "array" di nodi ognuno dei quali è composto di due parti, che si chiamano nome e valore.

Bene, una volta visto questo, cambio discorso e vado a vedere che succede se devo updatare un record.
Posso ottenere, con la sintassi vista prima, in cui leggo il contenuto del nodo principale, l'elenco dei children di questo nodo?
Vediamo:
        reference.addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                for (DataSnapshot child : dataSnapshot.getChildren()) {
                    System.out.println(child.getValue());
                }
            }

            @Override
            public void onCancelled(DatabaseError databaseError) {

            }
        });
10-22 14:57:18.235 10386-10386/? I/System.out: {cognome=Rossi, professione=Stupratore, nome=Mario, eta=45}
10-22 14:57:18.235 10386-10386/? I/System.out: {cognome=Bianchi, professione=Squartatore, nome=Carlo, eta=54}
10-22 14:57:18.235 10386-10386/? I/System.out: {cognome=Verdi, professione=Serial killer, nome=Luigi, eta=36}
Come avevo già, del resto, fatto.
Ma se io updato uno dei tre, ammettiamo che Luigi Verdi si sia ravveduto e adesso faccia un lavoro onesto, come faccio?
Come si fa una query su database Firebase?

sabato 22 ottobre 2016

Leggere i dati dal database Firebase in una classe predisposta.

E poi sono arrivato anche a leggere i dati dal database con questo codice:
public class MainActivity extends AppCompatActivity {

    Persona persona;
    DatabaseReference nuovo;
    Helper helper;
    Button button;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        helper=new Helper(this);
        final DatabaseReference databaseReference = FirebaseDatabase.getInstance().getReference();

        button=(Button)findViewById(R.id.button2);




        button.setOnClickListener(new View.OnClickListener(){

            @Override
            public void onClick(View v) {
                databaseReference.addValueEventListener(new ValueEventListener() {
                    @Override
                    public void onDataChange(DataSnapshot dataSnapshot) {
                        for(DataSnapshot record : dataSnapshot.getChildren()){
                           Persona p=record.getValue(Persona.class);
                            System.out.println("NOME "+p.nome+" - COGNOME "+p.cognome+" - PROFESSIONE "+p.professione);

                        }
                    }

                    @Override
                    public void onCancelled(DatabaseError databaseError) {

                    }
                });
            }
        });
    }

}

class Persona{
    public String nome;
    public String cognome;
    public int eta;
    public String professione;
    public Persona(){}
    public Persona(String nome, String cognome, int eta, String professione){
        this.nome=nome;
        this.cognome=cognome;
        this.eta=eta;
        this.professione=professione;
    }
}

class Helper extends SQLiteOpenHelper{

    Context context;
    public Helper(Context context) {
        super(context, "mioDatabase.db", null, 1);
        this.context=context;
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL("CREATE TABLE TABELLA(NOME TEXT, COGNOME TEXT, ETA INTEGER, PROFESSIONE TEXT)");
    }

    public void save(Persona p){
        SQLiteDatabase db=this.getWritableDatabase();
        ContentValues values=new ContentValues();
        values.put("NOME",p.nome);
        values.put("COGNOME",p.cognome);
        values.put("ETA",p.eta);
        values.put("PROFESSIONE",p.professione);
        db.insert("TABELLA",null,values);
    }

    public Cursor query(){
        SQLiteDatabase db=this.getWritableDatabase();
        Cursor crs=db.rawQuery("SELECT* FROM TABELLA",null);
        crs.moveToFirst();
        return crs;
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    }
}
Mi salvo il codice qui

venerdì 21 ottobre 2016

Leggere i dati in una istanza di classe dal database Firebase.

Ora mi devo occupare della parte inversa, ossia leggere i dati dal database remoto e portarli, per mezzo di una classe consona, su un database SQLite sul cellulare.
L'ultima parte si farà con il metodo helper.save(...) avente per parametro la classe stessa.
Il problema sta nella lettura dei dati.
Vediamo...

Ecco: bisogna aggiungere un listener (ho subordinato la lettura dei dati alla pressione su un button).
         button.setOnClickListener(new View.OnClickListener(){

            @Override
            public void onClick(View v) {
                databaseReference.addValueEventListener(new ValueEventListener() {
                    @Override
                    public void onDataChange(DataSnapshot dataSnapshot) {
                        System.out.println(dataSnapshot.getValue());
                    }

                    @Override
                    public void onCancelled(DatabaseError databaseError) {

                    }
                });
            }
        });
10-21 12:30:54.625 10720-10720/? I/System.out: {-KUae5VrVyXMk2lFt39t={cognome=Bianchi, professione=Ladro, nome=Carlo, eta=54}, -KUae5VW3jfCoWP2yQmc={cognome=Rossi, professione=Scassinatore, nome=Mario, eta=45}, -KUae5VsYGesGotl6PZf={cognome=Verdi, professione=Falsario, nome=Luigi, eta=36}}
Vediamo se riesco a essere un po' più specifico: qui ho aggiunto il listener a tutto il database.
Provo ad aggiungerlo a un singolo nodo...

La via migliore che ho trovato, non avendo predeterminati i nomi dei nodi, né potendovi costruire agevolmente un ciclo for...next, è quella di prendermi tutto il DataSnapshot, come da tutorial trovati, e separarlo nei singoli nodi mediante il getChildren().
        button.setOnClickListener(new View.OnClickListener(){

            @Override
            public void onClick(View v) {
                databaseReference.addValueEventListener(new ValueEventListener() {
                    @Override
                    public void onDataChange(DataSnapshot dataSnapshot) {
                        for(DataSnapshot record : dataSnapshot.getChildren()){
                            System.out.println(record.getValue());
                        }
                    }

                    @Override
                    public void onCancelled(DatabaseError databaseError) {

                    }
                });
            }
        });
Ed eccoli in sequenza:
10-21 13:00:57.645 15520-15520/? I/System.out: {cognome=Rossi, professione=Scassinatore, nome=Mario, eta=45}
10-21 13:00:57.645 15520-15520/? I/System.out: {cognome=Bianchi, professione=Ladro, nome=Carlo, eta=54}
10-21 13:00:57.645 15520-15520/? I/System.out: {cognome=Verdi, professione=Falsario, nome=Luigi, eta=36}

Ora devo solo vedere come convertire questi in istanze della classe Persona, da poter dare in bocca al mio metodo helper.save come parametri, e il gioco è fatto...

Dopo essere ammattito un po', ho trovato quello che serve a me.
E' più semplice di quello che sembrava:
        button.setOnClickListener(new View.OnClickListener(){

            @Override
            public void onClick(View v) {
                databaseReference.addValueEventListener(new ValueEventListener() {
                    @Override
                    public void onDataChange(DataSnapshot dataSnapshot) {
                        for(DataSnapshot record : dataSnapshot.getChildren()){
                           Persona p=record.getValue(Persona.class);
                            System.out.println("NOME "+p.nome+" - COGNOME "+p.cognome+" - PROFESSIONE "+p.professione);

                        }
                    }

                    @Override
                    public void onCancelled(DatabaseError databaseError) {

                    }
                });
            }
        });
E ottengo l'immediato cambiamento sul dispositivo quando vado a cambiare qualcosa nel database remoto.

Perfetto!

Primo trasferimento di SQLiteDatabase su Firebase!

Per ripasso

Codice per rinominare la "radice" del database:
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        DatabaseReference databaseReference=FirebaseDatabase.getInstance().getReference();
        databaseReference.setValue("Ciao, ciccio");


    }
Bene.
Ora provo, come da esempio a mettere una classe su un database.
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        
        Persona persona=new Persona("Mario","Rossi");

        DatabaseReference databaseReference= FirebaseDatabase.getInstance().getReference();
        databaseReference.setValue(persona);




    }

    class Persona{
        public String nome;
        public String cognome;

        public Persona(String nome, String cognome){
            this.nome=nome;
            this.cognome=cognome;
        }
    }
Ho usato il metodo setValue del DatabaseReference.
Ma leggo che si può usare anche il metodo push.
Vediamolo...

Ecco sì: questo è perfetto:
public class MainActivity extends AppCompatActivity {

    Persona persona;
    DatabaseReference nuovo;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);


        DatabaseReference databaseReference= FirebaseDatabase.getInstance().getReference();
        persona=new Persona("Mario","Rossi");
        nuovo=databaseReference.push();
        nuovo.setValue(persona);
        persona=new Persona("Giuseppe","Verdi");
        nuovo=databaseReference.push();
        nuovo.setValue(persona);
        persona=new Persona("Antonello", "Il Pazzo");
        nuovo=databaseReference.push();
        nuovo.setValue(persona);





    }

    class Persona{
        public String nome;
        public String cognome;

        public Persona(String nome, String cognome){
            this.nome=nome;
            this.cognome=cognome;
        }
    }


}


Ora mi creo un semplice database SQLite e tramite una classe intermedia lo salvo in remoto.
Prima mi compilo il database SQLite sul cellulare.
public class MainActivity extends AppCompatActivity {

    Persona persona;
    DatabaseReference nuovo;
    Helper helper;
    Button button;
    Button buttonSave;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        helper=new Helper(this);
        //DatabaseReference databaseReference = FirebaseDatabase.getInstance().getReference();

        button=(Button)findViewById(R.id.button2);
        buttonSave=(Button)findViewById(R.id.button3);



        buttonSave.setOnClickListener(new View.OnClickListener(){

            @Override
            public void onClick(View v) {
                persona=new Persona("Mario","Rossi",45,"Scassinatore");
                helper.save(persona);
                persona=new Persona("Carlo","Bianchi",54,"Ladro");
                helper.save(persona);
                persona=new Persona("Luigi","Verdi",36, "Falsario");
                helper.save(persona);
            }
        });
        button.setOnClickListener(new View.OnClickListener(){

            @Override
            public void onClick(View v) {
                Cursor c=helper.query();
                do{
                    System.out.println(c.getString(0)+" "+c.getString(1)+" "+c.getInt(2)+" "+c.getString(3));
                } while(c.moveToNext());
            }
        });
    }

}

class Persona{
    public String nome;
    public String cognome;
    public int eta;
    public String professione;
    public Persona(String nome, String cognome, int eta, String professione){
        this.nome=nome;
        this.cognome=cognome;
        this.eta=eta;
        this.professione=professione;
    }
}

class Helper extends SQLiteOpenHelper{

    Context context;
    public Helper(Context context) {
        super(context, "mioDatabase.db", null, 1);
        this.context=context;
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL("CREATE TABLE TABELLA(NOME TEXT, COGNOME TEXT, ETA INTEGER, PROFESSIONE TEXT)");
    }

    public void save(Persona p){
        SQLiteDatabase db=this.getWritableDatabase();
        ContentValues values=new ContentValues();
        values.put("NOME",p.nome);
        values.put("COGNOME",p.cognome);
        values.put("ETA",p.eta);
        values.put("PROFESSIONE",p.professione);
        db.insert("TABELLA",null,values);
    }

    public Cursor query(){
        SQLiteDatabase db=this.getWritableDatabase();
        Cursor crs=db.rawQuery("SELECT* FROM TABELLA",null);
        crs.moveToFirst();
        return crs;
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    }
}
(qui ho commentizzato il codice relativo a Firebase perché adesso devo solo creare un database, il codice cambierà in seguito).

Perfetto: il database è stato creato e compilato correttamente, ne ho la conferma:
10-21 11:40:44.305 4257-4257/? I/System.out: Mario Rossi 45 Scassinatore
10-21 11:40:44.305 4257-4257/? I/System.out: Carlo Bianchi 54 Ladro
10-21 11:40:44.305 4257-4257/? I/System.out: Luigi Verdi 36 Falsario
Ora viene la parte "tosta"!

Devo prendere un Cursor dal mio database.
Per ogni RECORD devo fare un push e quindi un setValue, ossia aggiungere un NODO al database.
Quindi scorrere il Cursor, porre i valori in un'istanza della classe Persona e quindi fare push e settare Value all'istanza di Persona.
Traduciamo in codice l'idea...

        button.setOnClickListener(new View.OnClickListener(){

            @Override
            public void onClick(View v) {
                Cursor c=helper.query();
                do{
                   persona=new Persona(c.getString(0),c.getString(1),c.getInt(2),c.getString(3));
                } while(c.moveToNext());
            }
        });
Ho creato un'istanza di Persona mettendo i valori dei campi come parametri del costruttore della classe.
Ora...
        button.setOnClickListener(new View.OnClickListener(){

            @Override
            public void onClick(View v) {
                Cursor c=helper.query();
                do{
                   persona=new Persona(c.getString(0),c.getString(1),c.getInt(2),c.getString(3));
                    DatabaseReference nodo=databaseReference.push();
                    nodo.setValue(persona);

                } while(c.moveToNext());
            }
        });
...questo dovrebbe funzionare. Vediamo...

ALLA GRANDE!!!