|
7. Grafos de Profundidad
Ya hemos visto como db4o maneja las asociaciones entre objetos, pero nuestro ejemplo es aún muy simple y trivial, comparado a los modelos de dominio del mundo real. En particular, no hemos visto como db4o se comporta ante la presencia de estructuras recursivas. Vamos a emular tal estructura reemplazando nuestra lista histórica con una lista enlazada implícitamente provista por la clase SensorReadout.
package com.db4o.f1.chapter5;
import java.util.*;
public class SensorReadout {
private Date time;
private Car car;
private String description;
private SensorReadout next;
protected SensorReadout(Date time,Car car,String description) {
this.time=time;
this.car=car;
this.description=description;
this.next=null;
}
public Car getCar() {
return car;
}
public Date getTime() {
return time;
}
public String getDescription() {
return description;
}
public SensorReadout getNext() {
return next;
}
public void append(SensorReadout readout) {
if(next==null) {
next=readout;
}
else {
next.append(readout);
}
}
public int countElements() {
return (next==null ? 1 : next.countElements()+1);
}
public String toString() {
return car+" : "+time+" : "+description;
}
}
| |
Nuestro auto ahora solo mantiene una asociación a un sensor de lectura 'cabecera'.
package com.db4o.f1.chapter5;
import java.util.*;
public class Car {
private String model;
private Pilot pilot;
private SensorReadout history;
public Car(String model) {
this.model=model;
this.pilot=null;
this.history=null;
}
public Pilot getPilot() {
return pilot;
}
public void setPilot(Pilot pilot) {
this.pilot=pilot;
}
public String getModel() {
return model;
}
public SensorReadout getHistory() {
return history;
}
public void snapshot() {
appendToHistory(new TemperatureSensorReadout(
new Date(),this,"oil",pollOilTemperature()));
appendToHistory(new TemperatureSensorReadout(
new Date(),this,"water",pollWaterTemperature()));
appendToHistory(new PressureSensorReadout(
new Date(),this,"oil",pollOilPressure()));
}
protected double pollOilTemperature() {
return 0.1*countHistoryElements();
}
protected double pollWaterTemperature() {
return 0.2*countHistoryElements();
}
protected double pollOilPressure() {
return 0.3*countHistoryElements();
}
public String toString() {
return model+"["+pilot+"]/"+countHistoryElements();
}
private int countHistoryElements() {
return (history==null ? 0 : history.countElements());
}
private void appendToHistory(SensorReadout readout) {
if(history==null) {
history=readout;
}
else {
history.append(readout);
}
}
}
| |
7.1. Almacenando y actualizando
Sin sorpresas por aquí.
[storeCar]
Pilot pilot=new Pilot("Rubens Barrichello",99);
Car car=new Car("BMW");
car.setPilot(pilot);
db.set(car);
| |
Now we would like to build a sensor readout chain. We already know about the update depth trap, so we configure this first.
[setCascadeOnUpdate]
Db4o.configure().objectClass(Car.class).cascadeOnUpdate(true);
| |
Recolectemos algunas lecturas del sensor.
[takeManySnapshots]
ObjectSet result=db.get(new Car(null));
Car car=(Car)result.next();
for(int i=0;i<5;i++) {
car.snapshot();
}
db.set(car);
| |
7.2. Recuperación
Ahora que tenemos una estructura suficientemente profunda, la recuperaremos de la base de datos y la recorreremos.
Primero verifiquemos que verdaderamente hemos tomado muchas capturas.
[retrieveAllSnapshots]
ObjectSet result=db.get(SensorReadout.class);
while(result.hasNext()) {
System.out.println(result.next());
}
| |
Todas estas lecturas pertenecen a una lista enlazada, así que debemos poder accederlas todas simplemente recorriendo la estructura de nuestra lista.
[retrieveSnapshotsSequentially]
ObjectSet result=db.get(new Car(null));
Car car=(Car)result.next();
SensorReadout readout=car.getHistory();
while(readout!=null) {
System.out.println(readout);
readout=readout.getNext();
}
| |
Ouch! ¿ Que esta pasando aca ?
7.2.1. Profundidad de activación
Deja vu - este es solo el otro lado de la profundidad de actualización.
db4o no puede llevar registro cuando se están recorriendo referencias de objetos recuperados de la base de datos. Por lo que siempre tendría que retornar grafos de objetos 'completos' en la recuperacion - en el peor de los casos esto se reduciria a introducir todo el contenido de la base de datos dentro de la memoria por una sola consulta.
Esto es absolutamente indeseable en la mayoría de las situaciones, entonces db4o provee un mecanismo que ofrece al cliente un control especifico sobre cuanto quiere sacar de la base de datos cuando se pide un objeto. Este mecanismo se llama profundidad de activación y funciona de forma muy similar a nuestra profundidad de actualizacion ya conocida.
La profundidad de activación por omisión para cualquier objeto es 5, así que nuestro ejemplo arriba se ejecuta dentro de valores nulos luego de recorrer 5 referencias.
Podemos consultar por objetos dinámicamente para activar sus referencias miembro. Esto nos permite recuperar cada lectura de sensor individual en la lista de la base de datos al momento que se necesite.
[retrieveSnapshotsSequentiallyImproved]
ObjectSet result=db.get(new Car(null));
Car car=(Car)result.next();
SensorReadout readout=car.getHistory();
while(readout!=null) {
db.activate(readout,1);
System.out.println(readout);
readout=readout.getNext();
}
| |
Observar que las referencias 'cortadas' también pueden influenciar el comportamiento de sus objetos: En este caso la longitud de la lista es calculada dinámicamente, y por eso es restringida por profundidad de activación.
En vez de activar elementos del subgrafo dinámicamente, tambien puede configurar la profunidad de activación estáticamente. Podemos decirle a nuestros objetos de clase SensorReadout que hagan cascada de las activaciones automáticamente, por ejemplo.
[setActivationDepth]
Db4o.configure().objectClass(TemperatureSensorReadout.class)
.cascadeOnActivate(true);
| |
[retrieveSnapshotsSequentially]
ObjectSet result=db.get(new Car(null));
Car car=(Car)result.next();
SensorReadout readout=car.getHistory();
while(readout!=null) {
System.out.println(readout);
readout=readout.getNext();
}
| |
Sin embargo debe ser muy cuidadoso. Las características de la activación son delicadas. Db4o proporciona un amplio rango de opciones de configuración para controlar la profundidad de activación a un nivel muy fino. Encontrará esos triggers en com.db4o.config.Configuration y las clases asociadas ObjectClass y ObjectField.
No olvide limpiar la base de datos.
[deleteAllObjects]
ObjectSet result=db.get(new Object());
while(result.hasNext()) {
db.delete(result.next());
}
| |
7.3. Conclusión
Ahora debemos tener las herramientas en mano para trabajar con grafos de objetos arbitrariamente complejos. Pero hasta ahora solo hemos trabajado hacia adelante, dando por sentado que los cambios que hacemos a nuestra preciosa data pool son correctos. Que pasa si tenemos que deshacer a un estado anterior gracias a alguna falla ? En el próximo capítulo vamos a introducir el concepto de transacción db4o.
7.4. Fuente completo
package com.db4o.f1.chapter5;
import java.io.*;
import com.db4o.*;
import com.db4o.f1.*;
public class DeepExample extends Util {
public static void main(String[] args) {
new File(Util.YAPFILENAME).delete();
ObjectContainer db=Db4o.openFile(Util.YAPFILENAME);
try {
storeCar(db);
db.close();
setCascadeOnUpdate();
db=Db4o.openFile(Util.YAPFILENAME);
takeManySnapshots(db);
db.close();
db=Db4o.openFile(Util.YAPFILENAME);
retrieveAllSnapshots(db);
db.close();
db=Db4o.openFile(Util.YAPFILENAME);
retrieveSnapshotsSequentially(db);
retrieveSnapshotsSequentiallyImproved(db);
db.close();
setActivationDepth();
db=Db4o.openFile(Util.YAPFILENAME);
retrieveSnapshotsSequentially(db);
deleteAllObjects(db);
}
finally {
db.close();
}
}
public static void storeCar(ObjectContainer db) {
Pilot pilot=new Pilot("Rubens Barrichello",99);
Car car=new Car("BMW");
car.setPilot(pilot);
db.set(car);
}
public static void setCascadeOnUpdate() {
Db4o.configure().objectClass(Car.class).cascadeOnUpdate(true);
}
public static void takeManySnapshots(ObjectContainer db) {
ObjectSet result=db.get(new Car(null));
Car car=(Car)result.next();
for(int i=0;i<5;i++) {
car.snapshot();
}
db.set(car);
}
public static void retrieveAllSnapshots(ObjectContainer db) {
ObjectSet result=db.get(SensorReadout.class);
while(result.hasNext()) {
System.out.println(result.next());
}
}
public static void retrieveSnapshotsSequentially(
ObjectContainer db) {
ObjectSet result=db.get(new Car(null));
Car car=(Car)result.next();
SensorReadout readout=car.getHistory();
while(readout!=null) {
System.out.println(readout);
readout=readout.getNext();
}
}
public static void retrieveSnapshotsSequentiallyImproved(
ObjectContainer db) {
ObjectSet result=db.get(new Car(null));
Car car=(Car)result.next();
SensorReadout readout=car.getHistory();
while(readout!=null) {
db.activate(readout,1);
System.out.println(readout);
readout=readout.getNext();
}
}
public static void setActivationDepth() {
Db4o.configure().objectClass(TemperatureSensorReadout.class)
.cascadeOnActivate(true);
}
public static void deleteAllObjects(ObjectContainer db) {
ObjectSet result=db.get(new Object());
while(result.hasNext()) {
db.delete(result.next());
}
}
}
| |
-- generated by Doctor courtesy of db4objecs Inc.
|