4. Objetos estructurados


Es tiempo de extender nuestro dominio con otra clase y ver como db4o maneja interrelaciones entre objetos. Demos a nuestro piloto un vehículo.


package com.db4o.f1.chapter2;

public class Car {   
    private String model;
    private Pilot pilot;
    
    public Car(String model) {
        this.model=model;
        this.pilot=null;
    }
      
    public Pilot getPilot() {
        return pilot;
    }
    
    public void setPilot(Pilot pilot) {
        this.pilot = pilot;
    }
    
    public String getModel() {
        return model;
    }
    
    public String toString() {
        return model+"["+pilot+"]";
    }
}




    4.1. Almacenando objetos estructurados


    Para almacenar un auto con su piloto, simplemente enviamos set() a nuestro objeto de más alto nivel, el auto. El piloto será almacenado implícitamente.


    [storeFirstCar]
    Car car1=new Car("Ferrari");
    Pilot pilot1=new Pilot("Michael Schumacher",100);
    car1.setPilot(pilot1);
    db.set(car1);


    De acuerdo, necesitamos algo de competencia. Esta vez almacenamos explícitamente el piloto antes de ingresar el auto - esto no hace ninguna diferencia.


    [storeSecondCar]
    Pilot pilot2=new Pilot("Rubens Barrichello",99);
    db.set(pilot2);
    Car car2=new Car("BMW");
    car2.setPilot(pilot2);
    db.set(car2);



    4.2. Recuperando objetos estructurados



      4.2.1. QBE


      Para recuperar todos los autos, simplemente proveemos un prototipo 'en blanco'.


      [retrieveAllCarsQBE]
      Car proto=new Car(null);
      ObjectSet result=db.get(proto);
      listResult(result);


      Por supuesto que también podemos consultar por todos los pilotos.


      [retrieveAllPilotsQBE]
      Pilot proto=new Pilot(null,0);
      ObjectSet result=db.get(proto);
      listResult(result);


      Ahora inicializemos nuestro prototipo para especificar todos los autos manejados por Rubens Barrichello.


      [retrieveCarByPilotQBE]
      Pilot pilotproto=new Pilot("Rubens Barrichello",0);
      Car carproto=new Car(null);
      carproto.setPilot(pilotproto);
      ObjectSet result=db.get(carproto);
      listResult(result);


      ¿ Y si queremos recuperar un piloto por el auto ? Simplemente no necesitamos eso - si ya conocemos el auto, tan solo le preguntamos por su piloto directamente.


      4.2.2. API de Consultas


      Para consultar por un auto dado el nombre de su piloto tenemos que descender un nivel de profundidad en nuestra consulta.


      [retrieveCarByPilotNameQuery]
      Query query=db.query();
      query.constrain(Car.class);
      query.descend("pilot").descend("name")
              .constrain("Rubens Barrichello");
      ObjectSet result=query.execute();
      listResult(result);


      Podemos restringir el campo del piloto con un prototipo para lograr el mismo resultado.


      [retrieveCarByPilotProtoQuery]
      Query query=db.query();
      query.constrain(Car.class);
      Pilot proto=new Pilot("Rubens Barrichello",0);
      query.descend("pilot").constrain(proto);
      ObjectSet result=query.execute();
      listResult(result);


      Hemos visto que descender dentro de una consulta nos da otra consulta. También puede haber observado que las asociaciones entre los nodos de consulta parecen como bidireccionales en nuestros diagramas. Vayamos contra la corriente.


      [retrievePilotByCarModelQuery]
      Query carquery=db.query();
      carquery.constrain(Car.class);
      carquery.descend("model").constrain("Ferrari");
      Query pilotquery=carquery.descend("pilot");
      ObjectSet result=pilotquery.execute();
      listResult(result);





    4.3. Actualizando objetos estructurados


    Para actualizar objetos estructurados en db4o, simplemente llamamos set() sobre ellos de nuevo.


    [updateCar]
    ObjectSet result=db.get(new Car("Ferrari"));
    Car found=(Car)result.next();
    found.setPilot(new Pilot("Somebody else",0));
    db.set(found);
    result=db.get(new Car("Ferrari"));
    listResult(result);


    Modifiquemos el piloto también.


    [updatePilotSingleSession]
    ObjectSet result=db.get(new Car("Ferrari"));
    Car found=(Car)result.next();
    found.getPilot().addPoints(1);
    db.set(found);
    result=db.get(new Car("Ferrari"));
    listResult(result);


    ¿ Lindo y simple no es así ? Pero un momento, hay algo demoníaco al acecho justo detrás de la esquina. Veamos que pasa si dividimos esta tarea en dos sesiones db4o separadas: En la primera modificamos nuestro piloto y actualizamos su auto, en la segunda consultamos por el auto otra vez.


    [updatePilotSeparateSessionsPart1]
    ObjectSet result=db.get(new Car("Ferrari"));
    Car found=(Car)result.next();
    found.getPilot().addPoints(1);
    db.set(found);



    [updatePilotSeparateSessionsPart2]
    ObjectSet result=db.get(new Car("Ferrari"));
    listResult(result);


    Parece que estamos en problemas. ¿ Que pasa aquí y que podemos hacer para solucionarlo ?


      4.3.1. Profundidad de actualización


      Imagine un objeto complejo con muchos miembros que a su vez tienen muchos miembros. Cuando actualizamos este objeto, db4o tendrá que actualizar todos sus hijos, nietos, etc. Esto plantea una penalidad de performance severa y no será necesario en la mayoría de los casos - en algunos, de cualquier forma, sí lo serán.

      Para poder manejar este dilema de forma tan flexible como sea posible, db4o introduce el concepto de profundidad de actualización para controlar cuan profundamente el árbol del miembro de un objeto será caminado en la actualización. La profundidad de actualización por defecto para todos los objetos es 1, esto quiere decir que solo los miembros primitivos y String serán actualizados, pero los cambios en los miembros de los objetos no serán reflejados.

      db4o provee formas para tener un muy buen control sobre la profundidad de actualización. Para nuestro problema aquí le avisaremos a db4o actualizar el grafo completo de objetos Car enviando cascadeOnUpdate() para esta clase por consiguiente.


      [updatePilotSeparateSessionsImprovedPart1]
      Db4o.configure().objectClass("com.db4o.f1.chapter2.Car")
              .cascadeOnUpdate(true);



      [updatePilotSeparateSessionsImprovedPart2]
      ObjectSet result=db.get(new Car("Ferrari"));
      Car found=(Car)result.next();
      found.getPilot().addPoints(1);
      db.set(found);



      [updatePilotSeparateSessionsImprovedPart3]
      ObjectSet result=db.get(new Car("Ferrari"));
      listResult(result);


      Así esta mucho mejor.

      Observar que la configuración del contenedor debe ser seteada antes de que el contenedor sea abierto.

      Cubriremos las profundidades de actualización tanto como otras características con grafos de objetos complejos y las opciones de configuración respectivas de db4o en más detalle en un capítulo posterior.


    4.4. Borrando objetos estructurados


    Como hemos visto, enviamos delete() a los objetos para deshacernos de ellos.


    [deleteFlat]
    ObjectSet result=db.get(new Car("Ferrari"));
    Car found=(Car)result.next();
    db.delete(found);
    result=db.get(new Car(null));
    listResult(result);


    Bien, el auto desapareció. ¿ Que pasa con los pilotos ?


    [retrieveAllPilotsQBE]
    Pilot proto=new Pilot(null,0);
    ObjectSet result=db.get(proto);
    listResult(result);


    Ok, no hay sorpresa - no esperamos que un piloto desaparezca cuando su auto es desarmado en la vida real. ¿ Pero qué si queremos que los hijos de un objeto sean eliminados en el borrado también ?


      4.4.1. Borrado recursivo


      Puede ya sospechar que el problema del borrado recursivo (y quizás su solución, también) es muy similar a nuestro pequeño problema de la actualización, y está en lo cierto. Configuremos db4o para borrar el piloto de un auto también, cuando el auto es eliminado.


      [deleteDeepPart1]
      Db4o.configure().objectClass("com.db4o.f1.chapter2.Car")
              .cascadeOnDelete(true);


      [deleteDeepPart2]
      ObjectSet result=db.get(new Car("BMW"));
      Car found=(Car)result.next();
      db.delete(found);
      result=db.get(new Car(null));
      listResult(result);


      Otra vez: Notar que toda la configuración debe tomar lugar antes de que el ObjectContainer sea abierto.

      Miremos a nuestros pilotos otra vez.


      [retrieveAllPilotsQBE]
      Pilot proto=new Pilot(null,0);
      ObjectSet result=db.get(proto);
      listResult(result);



      4.4.2. Borrado recursivo revisitado


      Pero espere - ¿ Que pasa si los hijos de un objeto eliminado aún son referenciados por otros objetos ?


      [deleteDeepRevisited]
      ObjectSet result=db.get(new Pilot("Michael Schumacher",0));
      Pilot pilot=(Pilot)result.next();
      Car car1=new Car("Ferrari");
      Car car2=new Car("BMW");
      car1.setPilot(pilot);
      car2.setPilot(pilot);
      db.set(car1);
      db.set(car2);
      db.delete(car2);
      result=db.get(new Car(null));
      listResult(result);



      [retrieveAllPilotsQBE]
      Pilot proto=new Pilot(null,0);
      ObjectSet result=db.get(proto);
      listResult(result);


      Houston, tenemos un problema - y no hay solución simple a mano. Actualmente db4o no verifica si los objetos a ser borrados son referenciados en algún otro lugar, así que por favor, sea muy cuidadoso cuando use esta característica.

      Limpiemos nuestra base de datos para el próximo capítulo.


      [deleteAll]
      ObjectSet cars=db.get(new Car(null));
      while(cars.hasNext()) {
          db.delete(cars.next());
      }
      ObjectSet pilots=db.get(new Pilot(null,0));
      while(pilots.hasNext()) {
          db.delete(pilots.next());
      }



    4.5. Conclusión


    Eso es todo con respecto a las asociaciones de objetos: Podemos engancharnos a un objeto raíz y descender por su grafo de referencia para especificar consultas. Pero que pasa con los objetos de múltiples valores como los vectores (Arrays) y colecciones (Collections) ? Cubriremos estos en el próximo capítulo .


    4.6. Fuentes completas



    package com.db4o.f1.chapter2;

    import java.io.File;

    import com.db4o.Db4o;
    import com.db4o.ObjectContainer;
    import com.db4o.ObjectSet;
    import com.db4o.f1.Util;
    import com.db4o.query.Query;


    public class StructuredExample extends Util {
        public static void main(String[] args) {
            new File(Util.YAPFILENAME).delete();
            ObjectContainer db=Db4o.openFile(Util.YAPFILENAME);
            try {
                storeFirstCar(db);
                storeSecondCar(db);
                retrieveAllCarsQBE(db);
                retrieveAllPilotsQBE(db);
                retrieveCarByPilotQBE(db);
                retrieveCarByPilotNameQuery(db);
                retrieveCarByPilotProtoQuery(db);
                retrievePilotByCarModelQuery(db);
                updateCar(db);
                updatePilotSingleSession(db);
                updatePilotSeparateSessionsPart1(db);
                db.close();
                db=Db4o.openFile(Util.YAPFILENAME);
                updatePilotSeparateSessionsPart2(db);
                db.close();
                updatePilotSeparateSessionsImprovedPart1();
                db=Db4o.openFile(Util.YAPFILENAME);
                updatePilotSeparateSessionsImprovedPart2(db);
                db.close();
                db=Db4o.openFile(Util.YAPFILENAME);
                updatePilotSeparateSessionsImprovedPart3(db);
                deleteFlat(db);
                db.close();
                deleteDeepPart1();
                db=Db4o.openFile(Util.YAPFILENAME);
                deleteDeepPart2(db);
                deleteDeepRevisited(db);
            }
            finally {
                db.close();
            }
        }
        
        public static void storeFirstCar(ObjectContainer db) {
            Car car1=new Car("Ferrari");
            Pilot pilot1=new Pilot("Michael Schumacher",100);
            car1.setPilot(pilot1);
            db.set(car1);
        }

        public static void storeSecondCar(ObjectContainer db) {
            Pilot pilot2=new Pilot("Rubens Barrichello",99);
            db.set(pilot2);
            Car car2=new Car("BMW");
            car2.setPilot(pilot2);
            db.set(car2);
        }

        public static void retrieveAllCarsQBE(ObjectContainer db) {
            Car proto=new Car(null);
            ObjectSet result=db.get(proto);
            listResult(result);
        }

        public static void retrieveAllPilotsQBE(ObjectContainer db) {
            Pilot proto=new Pilot(null,0);
            ObjectSet result=db.get(proto);
            listResult(result);
        }

        public static void retrieveCarByPilotQBE(
                ObjectContainer db) {
            Pilot pilotproto=new Pilot("Rubens Barrichello",0);
            Car carproto=new Car(null);
            carproto.setPilot(pilotproto);
            ObjectSet result=db.get(carproto);
            listResult(result);
        }
        
        public static void retrieveCarByPilotNameQuery(
                ObjectContainer db) {
            Query query=db.query();
            query.constrain(Car.class);
            query.descend("pilot").descend("name")
                    .constrain("Rubens Barrichello");
            ObjectSet result=query.execute();
            listResult(result);
        }

        public static void retrieveCarByPilotProtoQuery(
                    ObjectContainer db) {
            Query query=db.query();
            query.constrain(Car.class);
            Pilot proto=new Pilot("Rubens Barrichello",0);
            query.descend("pilot").constrain(proto);
            ObjectSet result=query.execute();
            listResult(result);
        }
       
        public static void retrievePilotByCarModelQuery(ObjectContainer db) {
            Query carquery=db.query();
            carquery.constrain(Car.class);
            carquery.descend("model").constrain("Ferrari");
            Query pilotquery=carquery.descend("pilot");
            ObjectSet result=pilotquery.execute();
            listResult(result);
        }

        
        public static void updateCar(ObjectContainer db) {
            ObjectSet result=db.get(new Car("Ferrari"));
            Car found=(Car)result.next();
            found.setPilot(new Pilot("Somebody else",0));
            db.set(found);
            result=db.get(new Car("Ferrari"));
            listResult(result);
        }

        public static void updatePilotSingleSession(
                    ObjectContainer db) {
            ObjectSet result=db.get(new Car("Ferrari"));
            Car found=(Car)result.next();
            found.getPilot().addPoints(1);
            db.set(found);
            result=db.get(new Car("Ferrari"));
            listResult(result);
        }

        public static void updatePilotSeparateSessionsPart1(
                    ObjectContainer db) {
         ObjectSet result=db.get(new Car("Ferrari"));
            Car found=(Car)result.next();
            found.getPilot().addPoints(1);
            db.set(found);
        }

        public static void updatePilotSeparateSessionsPart2(
                    ObjectContainer db) {
            ObjectSet result=db.get(new Car("Ferrari"));
            listResult(result);
        }

        public static void updatePilotSeparateSessionsImprovedPart1() {
            Db4o.configure().objectClass("com.db4o.f1.chapter2.Car")
                    .cascadeOnUpdate(true);        
        }

        public static void updatePilotSeparateSessionsImprovedPart2(
                    ObjectContainer db) {
            ObjectSet result=db.get(new Car("Ferrari"));
            Car found=(Car)result.next();
            found.getPilot().addPoints(1);
            db.set(found);
        }

        public static void updatePilotSeparateSessionsImprovedPart3(
                    ObjectContainer db) {
            ObjectSet result=db.get(new Car("Ferrari"));
            listResult(result);
        }

        public static void deleteFlat(ObjectContainer db) {
            ObjectSet result=db.get(new Car("Ferrari"));
            Car found=(Car)result.next();
            db.delete(found);
            result=db.get(new Car(null));
            listResult(result);
        }
        
        public static void deleteDeepPart1() {
            Db4o.configure().objectClass("com.db4o.f1.chapter2.Car")
                    .cascadeOnDelete(true);
        }

        public static void deleteDeepPart2(ObjectContainer db) {
            ObjectSet result=db.get(new Car("BMW"));
            Car found=(Car)result.next();
            db.delete(found);
            result=db.get(new Car(null));
            listResult(result);
        }

        public static void deleteDeepRevisited(ObjectContainer db) {
            ObjectSet result=db.get(new Pilot("Michael Schumacher",0));
            Pilot pilot=(Pilot)result.next();
            Car car1=new Car("Ferrari");
            Car car2=new Car("BMW");
            car1.setPilot(pilot);
            car2.setPilot(pilot);
            db.set(car1);
            db.set(car2);
            db.delete(car2);
            result=db.get(new Car(null));
            listResult(result);
        }
        
        public static void deleteAll(ObjectContainer db) {
            ObjectSet cars=db.get(new Car(null));
            while(cars.hasNext()) {
                db.delete(cars.next());
            }
            ObjectSet pilots=db.get(new Pilot(null,0));
            while(pilots.hasNext()) {
                db.delete(pilots.next());
            }
        }
    }





    --
    generated by
    Doctor courtesy of db4objecs Inc.