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.
namespace com.db4o.f1.chapter5
{
    using System;
    
    public abstract class SensorReadout
    {
        DateTime _time;
        Car _car;
        string _description;
        SensorReadout _next;
        
        protected SensorReadout(DateTime time, Car car, string description)
        {
            _time = time;
            _car = car;
            _description = description;
            _next = null;            
        }
        
        public Car Car
        {
            get
            {
                return _car;
            }
        }
        
        public DateTime Time
        {
            get
            {
                return _time;
            }
        }
        
        public SensorReadout Next
        {
            get
            {
                return _next;
            }
        }
        
        public void Append(SensorReadout sensorReadout)
        {
            if (_next == null)
            {
                _next = sensorReadout;
            }
            else
            {
                _next.Append(sensorReadout);
            }
        }
        
        public int CountElements()
        {
            return (_next == null ? 1 : _next.CountElements() + 1);
        }
        
        override public string ToString()
        {
            return _car + " : " + _time + " : " + _description;
        }
    }
}

Our car only maintains an association to a 'head' sensor readout now.
namespace com.db4o.f1.chapter5
{
    using System;
    
    public class Car
    {
        string _model;
        Pilot _pilot;
        SensorReadout _history;
        
        public Car(string model)
        {
            _model = model;
            _pilot = null;
            _history = null;
        }
        
        public Pilot Pilot
        {
            get
            {
                return _pilot;
            }
            
            set
            {
                _pilot = value;
            }
        }
        
        public string Model
        {
            get
            {
                return _model;
            }
        }
        
        public SensorReadout GetHistory()
        {
            return _history;
        }
        
        public void Snapshot()
        {        
            AppendToHistory(new TemperatureSensorReadout(
                DateTime.Now, this, "oil", PollOilTemperature()));
            AppendToHistory(new TemperatureSensorReadout(
                DateTime.Now, this, "water", PollWaterTemperature()));
            AppendToHistory(new PressureSensorReadout(
                DateTime.Now, this, "oil", PollOilPressure()));
        }
        protected double PollOilTemperature()
        {
            return 0.1*CountHistoryElements();
        }
        
        protected double PollWaterTemperature()
        {
            return 0.2*CountHistoryElements();
        }
        
        protected double PollOilPressure()
        {
            return 0.3*CountHistoryElements();
        }
        
        override 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.
    [storeCar]
    Pilot pilot = new Pilot("Rubens Barrichello", 99);
        Car car = new Car("BMW");
        car.Pilot = 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(typeof(Car)).cascadeOnUpdate(true);

    Let's collect a few sensor readouts.
    [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. 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.
    [retrieveAllSnapshots]
    ObjectSet result = db.get(typeof(SensorReadout));
        while (result.hasNext())
        {
            Console.WriteLine(result.next());
        }
    OUTPUT:
    BMW[Rubens Barrichello/99]/15 : Fri Jul 29 04:59:59 CEST 2005 : oil pressure : 4.2
    BMW[Rubens Barrichello/99]/15 : Fri Jul 29 04:59:59 CEST 2005 : water temp : 2.6
    BMW[Rubens Barrichello/99]/15 : Fri Jul 29 04:59:59 CEST 2005 : oil temp : 1.2000000000000002
    BMW[Rubens Barrichello/99]/15 : Fri Jul 29 04:59:59 CEST 2005 : oil pressure : 3.3
    BMW[Rubens Barrichello/99]/15 : Fri Jul 29 04:59:59 CEST 2005 : water temp : 2.0
    BMW[Rubens Barrichello/99]/15 : Fri Jul 29 04:59:59 CEST 2005 : oil temp : 0.9
    BMW[Rubens Barrichello/99]/15 : Fri Jul 29 04:59:59 CEST 2005 : oil temp : 0.30000000000000004
    BMW[Rubens Barrichello/99]/15 : Fri Jul 29 04:59:59 CEST 2005 : oil pressure : 0.6
    BMW[Rubens Barrichello/99]/15 : Fri Jul 29 04:59:59 CEST 2005 : oil pressure : 1.5
    BMW[Rubens Barrichello/99]/15 : Fri Jul 29 04:59:59 CEST 2005 : oil pressure : 2.4
    BMW[Rubens Barrichello/99]/15 : Fri Jul 29 04:59:59 CEST 2005 : water temp : 1.4000000000000001
    BMW[Rubens Barrichello/99]/15 : Fri Jul 29 04:59:59 CEST 2005 : oil temp : 0.6000000000000001
    BMW[Rubens Barrichello/99]/15 : Fri Jul 29 04:59:59 CEST 2005 : oil temp : 0.0
    BMW[Rubens Barrichello/99]/15 : Fri Jul 29 04:59:59 CEST 2005 : water temp : 0.8
    BMW[Rubens Barrichello/99]/15 : Fri Jul 29 04:59:59 CEST 2005 : water temp : 0.2

All these readouts belong to one linked list, so we should be able to access them all by just traversing our list structure.
[retrieveSnapshotsSequentially]
ObjectSet result = db.get(new Car(null));
    Car car = (Car)result.next();
    SensorReadout readout = car.GetHistory();
    while (readout != null)
    {
        Console.WriteLine(readout);
        readout = readout.Next;
    }
OUTPUT:
BMW[Rubens Barrichello/99]/15 : Fri Jul 29 04:59:59 CEST 2005 : oil temp : 0.0
BMW[Rubens Barrichello/99]/15 : Fri Jul 29 04:59:59 CEST 2005 : water temp : 0.2
BMW[Rubens Barrichello/99]/15 : Fri Jul 29 04:59:59 CEST 2005 : oil pressure : 0.6
BMW[Rubens Barrichello/99]/15 : Fri Jul 29 04:59:59 CEST 2005 : oil temp : 0.30000000000000004
BMW[Rubens Barrichello/99]/15 : Fri Jul 29 04:59:59 CEST 2005 : water temp : 0.8
BMW[Rubens Barrichello/99]/15 : Fri Jul 29 04:59:59 CEST 2005 : oil pressure : 1.5
BMW[Rubens Barrichello/99]/15 : Fri Jul 29 04:59:59 CEST 2005 : oil temp : 0.6000000000000001
BMW[Rubens Barrichello/99]/15 : Fri Jul 29 04:59:59 CEST 2005 : water temp : 1.4000000000000001
BMW[Rubens Barrichello/99]/15 : Fri Jul 29 04:59:59 CEST 2005 : oil pressure : 2.4
BMW[Rubens Barrichello/99]/15 : Fri Jul 29 04:59:59 CEST 2005 : oil temp : 0.9
BMW[Rubens Barrichello/99]/15 : Fri Jul 29 04:59:59 CEST 2005 : water temp : 2.0
BMW[Rubens Barrichello/99]/15 : Fri Jul 29 04:59:59 CEST 2005 : oil pressure : 3.3
BMW[Rubens Barrichello/99]/15 : Fri Jul 29 04:59:59 CEST 2005 : oil temp : 1.2000000000000002
BMW[Rubens Barrichello/99]/15 : Fri Jul 29 04:59:59 CEST 2005 : water temp : 2.6
BMW[Rubens Barrichello/99]/15 : Fri Jul 29 04:59:59 CEST 2005 : oil pressure : 4.2

Ouch! What's happening here?

7.3. Conclusion

Now we should have the tools at hand to work with arbitrarily complex object graphs. But so far we have only been working forward, hoping that the changes we apply to our precious data pool are correct. What if we have to roll back to a previous state due to some failure? In the next chapter  we will introduce the db4o transaction concept.

7.4. Full source

namespace com.db4o.f1.chapter5
{
    using System;
    using System.IO;
    using com.db4o;
    
    public class DeepExample : Util
    {
        public static void Main(string[] args)
        {
            File.Delete(Util.YapFileName);
            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.Pilot = pilot;
            db.set(car);
        }
        
        public static void setCascadeOnUpdate()
        {
            Db4o.configure().objectClass(typeof(Car)).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(typeof(SensorReadout));
            while (result.hasNext())
            {
                Console.WriteLine(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)
            {
                Console.WriteLine(readout);
                readout = readout.Next;
            }
        }
        
        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);
                Console.WriteLine(readout);
                readout = readout.Next;
            }
        }
        
        public static void setActivationDepth()
        {
            Db4o.configure().objectClass(typeof(TemperatureSensorReadout))
                .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 db4objects Inc.