7. Deep graphs


We have already seen how db4o handles object associations, but our running example is still quite flat and simple, compared to real-world domain models. In particular we haven't seen how db4o behaves in the presence of recursive structures. We will emulate such a structure by replacing our history list with a linked list implicitely provided by the SensorReadout class.


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;
    }
}


Our car only maintains an association to a 'head' sensor readout now.


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. Storing and updating


    No surprises here.


    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.


    Db4o.configure().objectClass(Car.class).cascadeOnUpdate(true);
        


    Let's collect a few sensor readouts.


    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. Retrieving


    Now that we have a sufficiently deep structure, we'll retrieve it from the database and traverse it.

    First let's verify that we indeed have taken lots of snapshots.


    ObjectSet result=db.get(SensorReadout.class);
    while(result.hasNext()) {
        System.out.println(result.next());
    }
        


    All these readouts belong to one linked list, so we should be able to access them all by just traversing our list structure.


    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! What's happening here?


      7.2.1. Activation depth


      Deja vu - this is just the other side of the update depth issue.

      db4o cannot track when you are traversing references from objects retrieved from the database. So it would always have to return 'complete' object graphs on retrieval - in the worst case this would boil down to pulling the whole database content into memory for a single query.

      This is absolutely undesirable in most situations, so db4o provides a mechanism to give the client fine-grained control over how much he wants to pull out of the database when asking for an object. This mechanism is calledactivation depthand works quite similar to our familiar update depth.

      The default activation depth for any object is 5, so our example above runs into nulls after traversing 5 references.

      We can dynamically ask objects to activate their member references. This allows us to retrieve each single sensor readout in the list from the database just as needed.


      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();
      }
          


      Note that 'cut' references may also influence the behavior of your objects: In this case the length of the list is calculated dynamically, and therefor constrained by activation depth.

      Instead of dynamically activating subgraph elements, you can configure activation depth statically, too. We can tell our SensorReadout class objects to cascade activation automatically, for example.


      Db4o.configure().objectClass(TemperatureSensorReadout.class)
              .cascadeOnActivate(true);
          



      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();
      }
          


      You have to be very careful, though. Activation issues are tricky. Db4o provides a wide range of configuration features to control activation depth at a very fine-grained level. You'll find those triggers in com.db4o.config.Configuration and the associated ObjectClass and ObjectField classes.

      Don't forget to clean up the database.


      ObjectSet result=db.get(new Object());
      while(result.hasNext()) {
          db.delete(result.next());
      }
          



    7.3. Conclusion


    That's it, folks. No, of course it isn't. There's much more to db4o we haven't covered yet: schema evolution, custom persistence for your classes, writing your own query objects, etc.

    This tutorial is work in progress. We will successively add chapters and incorporate feedback from the community into the existing chapters.

    We hope that this tutorial has helped to get you started with db4o. How should you continue now?

    -(Interactive version only)While this tutorial is basically sequential in nature, try to switch back and forth between the chapters and execute the sample snippets in arbitrary order. You will be working with the same database throughout; sometimes you may just get stuck or even induce exceptions, but you can always reset the database via the console window.

    - The examples we've worked through are included in your db4o distribution in full source code. Feel free to experiment with it.

    - I you're stuck, see if the FAQ can solve your problem, browse the information on our web site, check if your problem is submitted to Bugzilla yet or join our newsgroup at news.dbv4odev.com .


    7.4. Full source



    package com.db4o.f1.chapter5;

    import java.io.*;
    import com.db4o.*;


    public class DeepExample {
        private final static String FILENAME="f1.yap";
        
        public static void main(String[] args) {
            new File(FILENAME).delete();
            ObjectContainer db=Db4o.openFile(FILENAME);
            try {
                storeCar(db);
                db.close();
                setCascadeOnUpdate();
                db=Db4o.openFile(FILENAME);
                takeManySnapshots(db);
                db.close();
                db=Db4o.openFile(FILENAME);            
                retrieveAllSnapshots(db);
                db.close();
                db=Db4o.openFile(FILENAME);
                retrieveSnapshotsSequentially(db);
                retrieveSnapshotsSequentiallyImproved(db);
                db.close();
                setActivationDepth();
                db=Db4o.openFile(FILENAME);
                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.