In articoli precedenti apparsi su questo sito abbiamo già fatto la conoscenza di MongoDB e l’abbiamo visto in azione congiunta con Node.js. Parliamo di uno dei DBMS più utilizzati al mondo ed il più diffuso dell’emisfero NoSQL. Orientato ai documenti, offre la possibilità di immagazzinare i dati in formato JSON, pensandoli già come oggetti: ciò lo rende rapidamente integrabile nelle nostre applicazioni ed il driver Java che mostriamo oggi offre interessanti possibilità di impiego.
Iniziare il progetto: integrare il driver per MongoDB
Supponiamo di avere a disposizione un’installazione funzionante di MongoDB come abbiamo spiegato negli articoli precedenti. Il nostro progetto Java dovrà interagirvi e, per questo, sarà necessario integrare il driver. Potremo partire dalla pagina dei download dove troveremo le opportune informazioni sia per farlo tramite un tool di build automation come Maven (solitamente integrato nei più comuni IDE) sia, in maniera più tradizionale, procedendo allo scaricamento della libreria in formato jar.
L’esempio
Il nostro esempio mostrerà l’invio di comandi al database per popolarlo ed interrogarlo direttamente senza tediosi preamboli come la progettazione della struttura interna.
Supponiamo di dover gestire un sistema remoto che funga da banca dati centralizzata per le casse di pagamento collocate in attività commerciali. Ciascuna cassa invierà al database ogni nuovo importo accompagnato da un numero identificativo: consideriamo, per semplicità, di avere dieci casse numerate in modo progressivo a partire dalla numero 1. L’accoppiata di questi due valori sarà salvata in un documento, concetto che in MongoDB equivale al record di un database relazionale.
Il codice che presentiamo dovrà creare dei dati di test che potranno essere poi visualizzati sempre nell’ambito del programma o grazie ad uno strumento di gestione del database come RoboMongo, di cui abbiamo parlato sempre su queste pagine. Il programmino che vedremo qui è di puro carattere sperimentale quindi accumuleremo tutte le funzionalità all’interno di una sola classe, che chiameremo DbManager, e ne useremo un’altra, contenente solo un main per metterne alla prova i metodi.
Nella nostra classe DbManager, definiremo alcune costanti ed un paio di membri:
class DbManager
{
private static final String SERVER_IP="192.168.100.25";
private static final int TCP_PORT=27017;
private static final int NUM_CASSE=10;
private MongoClient mongoClient;
private DB db;
...
}
Con le costanti forniremo innanzitutto le coordinate di rete ove sarà reperibile il nostro server MongoDB in esecuzione mentre NUM_CASSE indicherà il numero di casse che il nostro sistema avrà a disposizione.
I due membri sono oggetti messi a disposizione direttamente dal driver MongoDB/Java. MongoClient è l’oggetto tramite il quale gestiremo tutto il dialogo con il database e avremo a disposizione una connessione. DB è la classe che rappresenta il database MongoDB con cui siamo connessi. All’interno del costruttore verranno opportunamente inizializzati:
public DbManager(String dbName)
{
try {
mongoClient = new MongoClient( SERVER_IP, TCP_PORT );
db = mongoClient.getDB( dbName );
inizializza();
} catch (UnknownHostException ex) {
System.out.println("Errore");
}
}
Come vediamo, MongoClient stabilisce una connessione e subito viene impiegato per ottenere un riferimento al database il cui nome ci è stato passato come parametro formale.
Il metodo inizializza(), invocato nel costruttore, crea una serie di dati fittizi ma, al contempo, ci offre la possibilità di vedere al lavoro diversi oggetti del driver MongoDB:
private void inizializza()
{
DBCollection coll = db.getCollection("introiti");
for(int i=1; i<1+NUM_CASSE; i++)
{
int numeroImporti=(int) Math.round((Math.random()*10000)%200);
for(int k=0;k<numeroImporti;k++)
{
BasicDBObject voce = new BasicDBObject()
.append("idCassa", i)
.append("importo", Math.round((Math.random()*7000)%350));
coll.insert(voce);
}
}
}
Questo codice, benchè finalizzato ad un puro esempio, è piuttosto esemplificativo. Mostra per prima cosa il concetto di Collection. Si tratta di un insieme di documenti – un pò la tabella del mondo relazionale – che viene ottenuta fornendone il nome che in questo caso è “introiti”. Subito dopo un ciclo for attiva il meccanismo per generare dati fittizi ed avremo una iterazione per ogni cassa che dovrà essere presente nel database. Per ogni cassa si svolgerà un altro ciclo la cui durata dipenderà dal numero – generato casualmente – di introiti di test che si vogliono inserire. Finalmente, arriviamo all’oggetto BasicDBObject che costituisce il documento da salvare. Come si vede l’approccio ai dati di MongoDB si offre facilmente all’interazione ad oggetti. Con il metodo append, in pratica, inseriamo un campo alla volta corrispondente ad una proprietà dell’oggetto JSON con cui viene salvato il documento. Una volta, pronto il nuovo oggetto sarà inserito nella collection tramite il metodo insert.
Le interrogazioni
Gli altri metodi che inseriremo nella classe DbManager ci permetteranno di svolgere delle interrogazioni in base ai nostri scopi. Per lo più dovremo attivare una query su una Collection tramite il metodo find e poi fruire dei dati con un Cursor, un puntatore al set di risultati. A find potremo passare un oggetto BasicDBObject, che conterrà le condizioni per il filtro.
Questo primo metodo eseguirà la lettura di tutti i dati relativi ad una cassa:
public void trovaPerCassa(int numero)
{
DBCursor cursor=db.getCollection("introiti").find(new BasicDBObject().append("idCassa", numero));
try
{
while(cursor.hasNext())
{
DBObject ob=cursor.next();
System.out.println((ob.get("idCassa")+" - "+ob.get("importo")));
}
}
finally
{
cursor.close();
}
}
Come si vede alla prima riga otteniamo un cursore che punta alla collection “introiti” chiedendo a find di cercare tutti gli oggetti che nella proprietà idCassa abbiano il numero che abbiamo passato in input. Il cursore generato potrà essere iterato con i metodi hasNext che controlla la presenza di ulteriori documenti da leggere e next che legge il successivo.
Una volta ottenuto un documento con il metodo next(), potremo interrogarne le proprietà come una normale mappa. Il tutto verrà incluso in un costrutto try…finally in modo da ottenere la chiusura del cursore anche in caso di errore.
Abbiamo utilizzato in pratica un oggetto BasicDBObject come una sorta di query in grado di raggruppare le condizioni cui devono rispondere i documenti per poter essere selezionati nel set finale di risultati. La condizione di ricerca che abbiamo utilizzato è di uguaglianza: abbiamo infatti richiesto che il numero identificativo della cassa, salvato nel documento, fosse uguale a quello passato al metodo.
Se volessimo cercare tutti gli importi salvati relativi ad una medesima cassa ma di controvalore non inferiore ad un determinato livello dovremmo passare alla nostra query un ulteriore parametro da confrontare con l’operatore “maggiore o uguale”: vediamolo nel prossimo metodo:
public void trovaImportiPerCassaConValoreMinimo(int numero, int minimo)
{
DBObject query=new BasicDBObject()
.append("idCassa", numero)
.append("importo", new BasicDBObject().append("$gte", minimo));
DBCursor cursor=db.getCollection("introiti").find(query);
try
{
while(cursor.hasNext())
{
DBObject ob=cursor.next();
System.out.println((ob.get("idCassa")+" - "+ob.get("importo")));
}
}
finally
{
cursor.close();
}
}
La struttura del metodo è analoga a quella del precedente, ciò che cambia è l’oggetto BasicDBObject che svolge, ancora una volta, il ruolo di query. La seconda invocazione di append esegue un controllo sull’importo verificando che superi il livello riportato dalla variabile di nome minimo. La costante “$gte” è uno degli operatori di confronto messi a disposizione da MongoDB e rappresenta proprio quel concetto di “maggiore o uguale” di cui abbiamo bisogno
Conclusioni
Per sperimentare questi metodi, possiamo creare un semplicissimo metodo main di questo tipo:
public class App
{
public static void main( String[] args )
{
// inizializzazione del sistema con la creazione di dati di prova
DbManager manager=new DbManager("importi");
// stampa degli introiti relativi alla cassa n. 2
manager.trovaPerCassa(2);
/* ricerca degli introiti registrati dalla cassa n. 2
* di controvalore non inferiore a 123,00 euro
*/
manager.trovaImportiPerCassaConValoreMinimo(2,123);
}
}
Il driver che abbiamo iniziato a vedere in questo tutorial permette di integrare velocemente MongoDB nei nostri programmi Java trasformando in persistenti gli oggetti che creiamo nelle nostre strutture dati. Continueremo a sperimentarlo nei prossimi metodi creandogli attorno contesti più concreti e vedendone ulteriori funzionalità.










No Responses to “MongoDB e Java: facciamoli interagire”