3.2 - Funcionalitat bàsica
Treballarem sobre un exemple anterior, el dels empleats, però incorporant més dades, com els telèfons, els correus electrònics, etc, per veure que podem guardar una classe un poc més complicada en la BD Orientada a Objectes.
En un projecte nou, anomenat Tema6_2, anem a incorporar les llibreries de DB4O. Aquestes es troben en el subdirectori lib del lloc on havíem col·locat DB4O.
Segurament serà suficient amb incorporar el jar
dg4o-8.0.249.16098-core-java5.jar
Si no tinguérem prou amb aquest, un candidat molt bo seria
dg4o-8.0.249.16098-all-java5.jar
I si en alguna ocasió tenim problemes, doncs sempre podem podem incorporar tots els jar en una llibreria d'usuari (per exemple amb el nom DB4O) com ja vam fer amb Hibernate. La imatge mostra el procés, però recordeu que segurament no farà falta:

Per organitzar-lo millor creem un paquet anomenat classesEmpleat , que ens servirà per a fer tots els exemples. Ens crearem la classe Empleat, i les classes Adreca i Telefon que utilitzarà aquella. Construïm les classes amb constructor i mètodes get i set.
private String carrer;
private String codipostal;
private String poblacio;
public Adreca() {
}
public Adreca(String c, String cp, String p) {
setCarrer(c);
setCodipostal(cp);
setPoblacio(p);
}
public String getCarrer() {
return carrer;
}
public void setCarrer(String carrer) {
this.carrer = carrer;
}
public String getCodipostal() {
return codipostal;
}
public void setCodipostal(String codipostal) {
this.codipostal = codipostal;
}
public String getPoblacio() {
return poblacio;
}
public void setPoblacio(String poblacio) {
this.poblacio = poblacio;
}
}
private boolean mobil;
private String numero;
public Telefon() {
}
public Telefon(boolean m, String num) {
setMobil(m);
setNumero(num);
}
public boolean isMobil() {
return mobil;
}
public void setMobil(boolean mobil) {
this.mobil = mobil;
}
public String getNumero() {
return numero;
}
public void setNumero(String numero) {
this.numero = numero;
}
}
Ara ja va Empleat:
No hem posat tots els mètodes get i set, per a que no quede tan llarg. Es poden generar molt fàcilment amb: Source --> Generate Getters and Setters
Com comentàvem anteriorment, hi ha una versió servidor, però que nosaltres ens conformarem amb la versió integrada (embedded). En el cas de la versió servidor utilitzaríem la classe Db4o per a fer la connexió. Com que nosaltres farem servir la versió integrada, utilitzarem la classe Db4oEmbedded.
Des de l’aplicació indicarem el nom del fitxer on es guardaran les dades cridant el mètode estàtic de Db4oEmbedded openFile. A partir d’aquest moment, es mantindrà oberta una transacció que continuarà activa fins que tanquem amb el mètode close.
Les proves que farem a continuació les podem posar en un paquet anomenat Exemples
Inserció
Per a guardar un objecte utilitzem el mètode store(objecte)
import com.db4o.ObjectContainer;
import classesEmpleat.Adreca;
import classesEmpleat.Empleat;
import classesEmpleat.Telefon;
public class Prova1 {
public static void main(String[] args) {
ObjectContainer bd = Db4oEmbedded.openFile("Empleats.db4o");
Empleat e = new Empleat("11111111a", "Albert", 10, 45, 1000, null, null, null, null, null);
// les dades més complicades les introduïm de forma especial
e.setAdreca(new Adreca("C/ Major, 7", "12001", "Castelló"));
String[] corr = { "alu11111111a@ieselcaminas.org" };
e.setCorreus_e(corr);
Telefon[] tels = { new Telefon(true, "666777888"), new Telefon(false, "964112233") };
e.setTelefons(tels);
bd.store(e);
bd.close();
}
}
Sí que ha guardat l'objecte. Mirem-lo des de la perspectiva OME amb un vídeo il·lustratiu:
En finalitzar d'observar les dades des de la perspectiva OME, és convenient tancar la connexió. Si no la tanquem, quan anem a executar el següent programa, ens donarà error, avisant que la Base de Dades està bloquejada (com.db4o.ext.DatabaseFileLockedException).
Per tant, haurem de tenir especial atenció a tancar la connexió a la Base de Dades. Podria passar que ens donara un error el programa, i la connexió s'haja quedat oberta. Segurament el més oportú serà intentar tancar el programa, o tancar Eclipse, i d'aquesta manera desbloquejarem la Base de Dades.
El mètode commit obliga a guardar les dades cap al contenidor i activa de nou una transacció per a les properes operacions, per tant és convenient anar utilitzant-lo després d'una sèrie d'actualitzacions.
Anem a posar algunes dades més, per a tenir un poc més de joc. Concretament seran dues empleades més.
import com.db4o.ObjectContainer;
import classesEmpleat.Adreca;
import classesEmpleat.Empleat;
import classesEmpleat.Telefon;
public class Prova1_1 {
public static void main(String[] args) {
ObjectContainer bd = Db4oEmbedded.openFile("Empleats.db4o");
Empleat e = new Empleat("22222222b","Berta",10,35,1700,null,null,null,null,null);
Empleat f = new Empleat("33333333c","Clàudia",20,37,1500,null,null,null,null,null);
//les dades més complicades les introduïm de forma especial
e.setAdreca(new Adreca("C/ Enmig, 7","12001","Castelló"));
String[] corr = {"alu22222222b@ieselcaminas.org","berta@gmail.com"};
e.setCorreus_e(corr);
Telefon[] tels = {new Telefon(true,"666555444"),new Telefon(false,"964223344")};
e.setTelefons(tels);
f.setAdreca(new Adreca("C/ de Dalt, 7",null,"Borriana"));
String[] corr2 = {"alu33333333c@ieselcaminas.org"};
f.setCorreus_e(corr2);
bd.store(e);
bd.store(f);
bd.close();
}
}
Consulta bàsica
En la següent pregunta, veurem les maneres de fer una consulta, però ara anem a veure la forma més senzilla, que és la realitzada a través del mètode anomenat queryByExample. Aquest mètode rep per paràmetre un objecte del tipus a cercar, que farà d’exemple o patró per trobar totes aquelles instàncies emmagatzemades coincidents amb les dades del patró. El patró que es passe per paràmetre no haurà de tenir totes les dades complimentades, sinó només aquelles de les quals se’n desitge la coincidència. Així, per exemple, si hi passem un objecte comercial amb un únic atribut complet (el nif), queryByExample retornarà totes aquelles instàncies que tinguin per nif el valor entrat. El retorn es fa en un objecte de tipus ObjectSet, una classe que implementa la interfície List de Java i també la interfície Iterable, de manera que siga possible recórrer el contingut usant els mètodes next i hasNext. També podem utilitzar el bucle for (el del foreach).
En el següent exemple es veu com una vegada obtingut l'objecte, es pot accedir molt fàcilment a tota la informació:
import com.db4o.ObjectContainer;
import com.db4o.ObjectSet;
import classesEmpleat.Empleat;
public class Prova2 {
public static void main(String[] args) {
ObjectContainer bd = Db4oEmbedded.openFile("Empleats.db4o");
Empleat e = null;
ObjectSet<Empleat> llista = bd.queryByExample(new Empleat("11111111a"));
if (llista.hasNext()) {
e = llista.next();
System.out.println("Nif: " + e.getNif() + ". Nom: " + e.getNom() + ". Població: " + e.getAdreca().getPoblacio());
System.out.println("Primer correu: " + e.getCorreus_e()[0] + ". Primer telèfon: " + e.getTelefons()[0].getNumero());
}
bd.close();
}
}
Observeu com no hem utilitzat un bucle per a recórrer la llista, sinó un if. Això és perquè en aquest cas concret sabem a priori que en cas de trobar alguna instància, només serà una. Aquest seria el resultat:
Nif: 11111111a. Nom: Albert. Població: Castelló
Primer correu: alu11111111a@ieselcaminas.org. Primer telèfon: 666777888
Esborrat
Per a poder fer una actualització o esborrat d'algun objecte de la Base de Dades, aquest s'ha de correspondre amb algun objecte del programa Java. Aquesta correspondència pot ser perquè un objecte nou l'hem guardat amb store() (i continua "viu"), o perquè l'hem llegit de la BD (millor dit, hem llegit una llista i després hem fet l'assignació a un objecte).
L’eliminació dels objectes s’aconsegueix amb el mètode delete. Per defecte, DB4O elimina només l'objecte que es passa com a paràmetre, però no els objectes que aquest puga contenir. Si un objecte conté un altre objecte, com succeeix amb les instàncies Empleat i Adreca i Telefon, això pot convertir-se en un gran problema, ja que són objectes que normalment no es manipularan per separat i, en cas que no s’esborren amb el seu propietari, continuaran indefinidament en la Base de Dades. Per evitar-lo hauríem de configurar per a que esborre en cascada.
Mirem un exemple en el qual esborrem un empleat. En el comentari teniu el moment en que encara no es corresponen, i per tant no es pot esborrar.
import com.db4o.ObjectContainer;
import com.db4o.ObjectSet;
import classesEmpleat.Empleat;
public class Prova3 {
public static void main(String[] args) {
ObjectContainer bd = Db4oEmbedded.openFile("Empleats.db4o");
Empleat e = new Empleat("22222222b");
// Si posàrem ací db.delete(e) no tindría efecte, perquè e no es
// correspon amb cap instància de la BD
ObjectSet<Empleat> llista = bd.queryByExample(e);
if (llista.hasNext()) {
e = llista.next();
bd.delete(e);
}
bd.close();
}
}
Com que no hem esborrat en cascada, si després mirem des de la perspectiva OME, comprovarem que encara existeixen els objectes adreça i telèfon, i que ara no correspondran a cap empleat. Hauríem d'aprofitar el moment per a esborrar des de la perspectiva OME les instàncies de Adreca i Telefon que corresponien a l'empleat que hem esborrat, per deixar-lo consistent. En les següents imatges es mostra aquest fet:

Ja no existeix l'objete Empleat corresponent a Berta, però:
![]() |
![]() |
encara existeix la seua adreça (C/Enmig, 7 de Castelló) i els seua telàfons (666555444 i 964223344)
Per a poder esborrar en cascada, en el moment d’obrir el fitxer contenidor haurem d'especificar-lo posant-li una configuració com veurem a continuació. No és possible modificar la configuració de forma dinàmica. A més, malauradament, la configuració no es guarda amb el fitxer contenidor, sinó que cada vegada que obrim, haurem d'especificar-li la configuració desitjada. En aquesta configuració li direm que la classe Empleat esborra en cascada, és a dir, que quan esborrem un objecte, els objectes "subordinats" (de les classes Adreca i Telefon) també s'esborraran.
import com.db4o.ObjectContainer;
import com.db4o.ObjectSet;
import com.db4o.config.EmbeddedConfiguration;
import classesEmpleat.Empleat;
public class Prova4 {
public static void main(String[] args) {
EmbeddedConfiguration conf = Db4oEmbedded.newConfiguration();
conf.common().objectClass(Empleat.class).cascadeOnDelete(true);
ObjectContainer bd = Db4oEmbedded.openFile(conf, "Empleats.db4o");
Empleat e = new Empleat("33333333c");
ObjectSet<Empleat> llista = bd.queryByExample(e);
if (llista.hasNext()) {
e = llista.next();
bd.delete(e);
}
bd.close();
}
}
En les següents imatges es mostra com ara sí que ha esborrat en cascada:

Hem esborrat a Clàudia
![]() |
![]() |
I també ha desaparegut la seua adreça (C/ de Dalt de Borriana). Com que no tenia telèfons, continuen els mateixos d'abans
Modificació
Per a modificar un objecte de la Base de Dades primer haurem de tenir un objecte de Java que es corresponga amb ell (igual que en l'esborrat). Després de modificar-lo, només l'haurem de guardar amb store(). Hem de parar atenció a que si el que volem modificar és d'una subclasse, haurem de modificar en cascada, sinó no tindrà efecte. Ho farem indicant cascadeOnUpdate(true) a la configuració amb què obrirem el fitxer :
import com.db4o.ObjectContainer;
import com.db4o.ObjectSet;
import com.db4o.config.EmbeddedConfiguration;
import classesEmpleat.Adreca;
import classesEmpleat.Empleat;
public class Prova5 {
public static void main(String[] args) {
EmbeddedConfiguration conf = Db4oEmbedded.newConfiguration();
conf.common().objectClass(Empleat.class).cascadeOnUpdate(true);
ObjectContainer bd = Db4oEmbedded.openFile(conf, "Empleats.db4o");
Empleat e = new Empleat("11111111a");
ObjectSet<Empleat> llista = bd.queryByExample(e);
if (llista.hasNext()) {
e = llista.next();
e.setSou(e.getSou() + 200);
Adreca adr = e.getAdreca();
adr.setCarrer("Pl. Rei en Jaume, 15");
adr.setCodipostal("12002");
e.setAdreca(adr);
bd.store(e);
}
bd.close();
}
}
En la imatge es veu que en fer l'actualització en cascada sí que s'han guardat els canvis, i la primera adreça (que és la corresponent a Albert) s'ha modificat.

La restricció que hem comentat abans de que hem de tenir un objecte de Java que es corresponga amb ell (que ocupa els casos d'esborrat i modificació), l'hem de tenir molt present. I hem d'anar amb compte, perquè quan es tanca la BD es perd tota correspondència.
El següent exemple és idèntic a l'anterior, però es tanca i es torna a obrir la BD després d'haver assignat a e l'objecte, i abans de guardar-lo. En principi el que voldríem és modificar les dades de l'empleat existent, però en realitat ham introduït un nou empleat (amb el mateix nif, nom, ...), i per tant molt perillós perquè estam duplicant la informació. Observeu que, com que només es vol modificar el sou, no cal actualitzar en cascada.
import com.db4o.ObjectContainer;
import com.db4o.ObjectSet;
import classesEmpleat.Empleat;
public class Prova6 {
public static void main(String[] args) {
ObjectContainer bd = Db4oEmbedded.openFile("Empleats.db4o");
Empleat e = new Empleat("11111111a");
ObjectSet<Empleat> llista = bd.queryByExample(e);
if (llista.hasNext()) {
e = llista.next();
e.setSou(e.getSou() + 200);
bd.close(); // Tanquem i tornem a obrir la BD, per veure que hem
// perdut la correspondència de e amb l'objecte de la BD
bd = Db4oEmbedded.openFile(Db4oEmbedded.newConfiguration(), "Empleats.db4o");
bd.store(e);
}
bd.close();
}
}
Ara el contingut de la Base de Dade és aquest:

On es veu que hem creat un nou objecte, en compte de modificar el que ja existia. I el mateix amb l'adreça i els telèfons
![]() |
![]() |
En cas que tanquem la BD i volem modificar o esborrar un objecte haurem de tornar a connectar amb ell.
I en el cas de la inserció, abans d'inserir, podríem comprovar que no existeix (per exemple que no existeix cap empleat amb aquest nif).
Llicenciat sota la Llicència Creative Commons Reconeixement NoComercial CompartirIgual 2.5





