4. Structured objects


It's time to extend our business domain with another class and see how db4o handles object interrelations. Let's give our pilot a vehicle.

namespace Db4objects.Db4o.Tutorial.F1.Chapter2
{
    public class Car
    {
        string _model;
        Pilot _pilot;
        
        public Car(string model)
        {
            _model = model;
            _pilot = null;
        }
      
        public Pilot Pilot
        {
            get
            {
                return _pilot;
            }
            
            set
            {
                _pilot = value;
            }
        }
        
        public string Model         
        {
            get
            {
                return _model;
            }
        }
        
        override public string ToString()
        {
            return string.Format("{0}[{1}]", _model, _pilot);
        }
    }
}



    4.1. Storing structured objects


    To store a car with its pilot, we just call SET() on our top level object, the car. The pilot will be stored implicitly.

    // storeFirstCar
    Car car1 = new Car("Ferrari");
        Pilot pilot1 = new Pilot("Michael Schumacher", 100);
        car1.Pilot = pilot1;
        db.Set(car1);


    Of course, we need some competition here. This time we explicitly store the pilot before entering the car - this makes no difference.

    // storeSecondCar
    Pilot pilot2 = new Pilot("Rubens Barrichello", 99);
        db.Set(pilot2);
        Car car2 = new Car("BMW");
        car2.Pilot = pilot2;
        db.Set(car2);



    4.2. Retrieving structured objects



      4.2.1. QBE


      To retrieve all cars, we simply provide a 'blank' prototype.

      // retrieveAllCarsQBE
      Car proto = new Car(null);
          IObjectSet result = db.Get(proto);
          ListResult(result);
      OUTPUT:
      2
      BMW[Rubens Barrichello/99]
      Ferrari[Michael Schumacher/100]


We can also query for all pilots, of course.

// retrieveAllPilotsQBE
Pilot proto = new Pilot(null, 0);
    IObjectSet result = db.Get(proto);
    ListResult(result);
OUTPUT:
2
Michael Schumacher/100
Rubens Barrichello/99


Now let's initialize our prototype to specify all cars driven by Rubens Barrichello.

// retrieveCarByPilotQBE
Pilot pilotproto = new Pilot("Rubens Barrichello",0);
    Car carproto = new Car(null);
    carproto.Pilot = pilotproto;
    IObjectSet result = db.Get(carproto);
    ListResult(result);
OUTPUT:
1
BMW[Rubens Barrichello/99]


What about retrieving a pilot by car? We simply don't need that - if we already know the car, we can simply access the pilot field directly.


4.2.2. Native Queries


Using native queries with constraints on deep structured objects is straightforward, you can do it just like you would in plain other code.

Let's constrain our query to only those cars driven by a Pilot with a specific name:


// retrieveCarsByPilotNameNative
string pilotName = "Rubens Barrichello";
    IObjectSet results = db.Query(new RetrieveCarsByPilotNamePredicate(pilotName));
    ListResult(results);
OUTPUT:
1
BMW[Rubens Barrichello/99]






4.2.3. SODA Query API


In order to use SODA for querying for a car given its pilot's name we have to descend two levels into our query.

// retrieveCarByPilotNameQuery
IQuery query = db.Query();
    query.Constrain(typeof(Car));
    query.Descend("_pilot").Descend("_name")
        .Constrain("Rubens Barrichello");
    IObjectSet result = query.Execute();
    ListResult(result);
OUTPUT:
1
BMW[Rubens Barrichello/99]


We can also constrain the pilot field with a prototype to achieve the same result.

// retrieveCarByPilotProtoQuery
IQuery query = db.Query();
    query.Constrain(typeof(Car));
    Pilot proto = new Pilot("Rubens Barrichello", 0);
    query.Descend("_pilot").Constrain(proto);
    IObjectSet result = query.Execute();
    ListResult(result);
OUTPUT:
1
BMW[Rubens Barrichello/99]


We have seen that descending into a query provides us with another query. Starting out from a query root we can descend in multiple directions. In practice this is the same as ascending from one child to a parent and descending to another child. We can conclude that queries turn one-directional references in our objects into true relations. Here is an example that queries for "a Pilot that is being referenced by a Car, where the Car model is 'Ferrari'":

// retrievePilotByCarModelQuery
IQuery carQuery = db.Query();
    carQuery.Constrain(typeof(Car));
    carQuery.Descend("_model").Constrain("Ferrari");
    IQuery pilotQuery = carQuery.Descend("_pilot");
    IObjectSet result = pilotQuery.Execute();
    ListResult(result);
OUTPUT:
1
Michael Schumacher/100





4.3. Updating structured objects


To update structured objects in db4o, we simply call SET() on them again.

// updateCar
IObjectSet result = db.Get(new Car("Ferrari"));
    Car found = (Car)result.Next();
    found.Pilot = new Pilot("Somebody else", 0);
    db.Set(found);
    result = db.Get(new Car("Ferrari"));
    ListResult(result);
OUTPUT:
1
Ferrari[Somebody else/0]


Let's modify the pilot, too.

// updatePilotSingleSession
IObjectSet result = db.Get(new Car("Ferrari"));
    Car found = (Car)result.Next();
    found.Pilot.AddPoints(1);
    db.Set(found);
    result = db.Get(new Car("Ferrari"));
    ListResult(result);
OUTPUT:
1
Ferrari[Somebody else/1]


Nice and easy, isn't it? But wait, there's something evil lurking right behind the corner. Let's see what happens if we split this task in two separate db4o sessions: In the first we modify our pilot and update his car:

// updatePilotSeparateSessionsPart1
IObjectSet result = db.Get(new Car("Ferrari"));
    Car found = (Car)result.Next();
    found.Pilot.AddPoints(1);
    db.Set(found);


And in the second, we'll double-check our modification:

// updatePilotSeparateSessionsPart2
IObjectSet result = db.Get(new Car("Ferrari"));
    ListResult(result);
OUTPUT:
1
Ferrari[Somebody else/2]


Looks like we're in trouble: Why did the Pilot's points not change? What's happening here and what can we do to fix it?


4.4. Deleting structured objects


As we have already seen, we call DELETE() on objects to get rid of them.

// deleteFlat
IObjectSet result = db.Get(new Car("Ferrari"));
    Car found = (Car)result.Next();
    db.Delete(found);
    result = db.Get(new Car(null));
    ListResult(result);
OUTPUT:
1
BMW[Rubens Barrichello/99]


Fine, the car is gone. What about the pilots?

// retrieveAllPilotsQBE
Pilot proto = new Pilot(null, 0);
    IObjectSet result = db.Get(proto);
    ListResult(result);
OUTPUT:
1
Rubens Barrichello/99


Ok, this is no real surprise - we don't expect a pilot to vanish when his car is disposed of in real life, too. But what if we want an object's children to be thrown away on deletion, too?


4.5. Conclusion


So much for object associations: We can hook into a root object and climb down its reference graph to specify queries. But what about multi-valued objects like arrays and collections? We will cover this in the next chapter .


4.6. Full source


using System;
using System.IO;
using Db4objects.Db4o;
using Db4objects.Db4o.Query;
namespace Db4objects.Db4o.Tutorial.F1.Chapter2
{    
    public class StructuredExample : Util
    {
        public static void Main(String[] args)
        {
            File.Delete(Util.YapFileName);
            
            IObjectContainer db = Db4oFactory.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=Db4oFactory.OpenFile(Util.YapFileName);
                UpdatePilotSeparateSessionsPart2(db);
                db.Close();
                UpdatePilotSeparateSessionsImprovedPart1(db);
                db=Db4oFactory.OpenFile(Util.YapFileName);
                UpdatePilotSeparateSessionsImprovedPart2(db);
                db.Close();
                db=Db4oFactory.OpenFile(Util.YapFileName);
                UpdatePilotSeparateSessionsImprovedPart3(db);
                DeleteFlat(db);
                db.Close();
                DeleteDeepPart1(db);
                db=Db4oFactory.OpenFile(Util.YapFileName);
                DeleteDeepPart2(db);
                DeleteDeepRevisited(db);
            }
            finally
            {
                db.Close();
            }
        }
        
        public static void StoreFirstCar(IObjectContainer db)
        {
            Car car1 = new Car("Ferrari");
            Pilot pilot1 = new Pilot("Michael Schumacher", 100);
            car1.Pilot = pilot1;
            db.Set(car1);
        }
        
        public static void StoreSecondCar(IObjectContainer db)
        {
            Pilot pilot2 = new Pilot("Rubens Barrichello", 99);
            db.Set(pilot2);
            Car car2 = new Car("BMW");
            car2.Pilot = pilot2;
            db.Set(car2);
        }
        public static void RetrieveAllCarsQBE(IObjectContainer db)
        {
            Car proto = new Car(null);
            IObjectSet result = db.Get(proto);
            ListResult(result);
        }
        
        public static void RetrieveAllPilotsQBE(IObjectContainer db)
        {
            Pilot proto = new Pilot(null, 0);
            IObjectSet result = db.Get(proto);
            ListResult(result);
        }
        
        public static void RetrieveCarByPilotQBE(IObjectContainer db)
        {
            Pilot pilotproto = new Pilot("Rubens Barrichello",0);
            Car carproto = new Car(null);
            carproto.Pilot = pilotproto;
            IObjectSet result = db.Get(carproto);
            ListResult(result);
        }
        
        public static void RetrieveCarByPilotNameQuery(IObjectContainer db)
        {
            IQuery query = db.Query();
            query.Constrain(typeof(Car));
            query.Descend("_pilot").Descend("_name")
                .Constrain("Rubens Barrichello");
            IObjectSet result = query.Execute();
            ListResult(result);
        }
        
        public static void RetrieveCarByPilotProtoQuery(IObjectContainer db)
        {
            IQuery query = db.Query();
            query.Constrain(typeof(Car));
            Pilot proto = new Pilot("Rubens Barrichello", 0);
            query.Descend("_pilot").Constrain(proto);
            IObjectSet result = query.Execute();
            ListResult(result);
        }
        
        public static void RetrievePilotByCarModelQuery(IObjectContainer db)
        {
            IQuery carQuery = db.Query();
            carQuery.Constrain(typeof(Car));
            carQuery.Descend("_model").Constrain("Ferrari");
            IQuery pilotQuery = carQuery.Descend("_pilot");
            IObjectSet result = pilotQuery.Execute();
            ListResult(result);
        }
        
        public static void RetrieveAllPilots(IObjectContainer db)
        {
            IObjectSet results = db.Get(typeof(Pilot));
            ListResult(results);
        }
        public static void RetrieveAllCars(IObjectContainer db)
        {
            IObjectSet results = db.Get(typeof(Car));
            ListResult(results);
        }
    
        public class RetrieveCarsByPilotNamePredicate : Predicate
        {
            readonly string _pilotName;
            
            public RetrieveCarsByPilotNamePredicate(string pilotName)
            {
                _pilotName = pilotName;
            }
            
            public bool Match(Car candidate)
            {
                return candidate.Pilot.Name == _pilotName;
            }
        }
    
        public static void RetrieveCarsByPilotNameNative(IObjectContainer db)
        {
            string pilotName = "Rubens Barrichello";
            IObjectSet results = db.Query(new RetrieveCarsByPilotNamePredicate(pilotName));
            ListResult(results);
        }
          
        public static void UpdateCar(IObjectContainer db)
        {
            IObjectSet result = db.Get(new Car("Ferrari"));
            Car found = (Car)result.Next();
            found.Pilot = new Pilot("Somebody else", 0);
            db.Set(found);
            result = db.Get(new Car("Ferrari"));
            ListResult(result);
        }
        
        public static void UpdatePilotSingleSession(IObjectContainer db)
        {
            IObjectSet result = db.Get(new Car("Ferrari"));
            Car found = (Car)result.Next();
            found.Pilot.AddPoints(1);
            db.Set(found);
            result = db.Get(new Car("Ferrari"));
            ListResult(result);
        }
        
        public static void UpdatePilotSeparateSessionsPart1(IObjectContainer db)
        {
            IObjectSet result = db.Get(new Car("Ferrari"));
            Car found = (Car)result.Next();
            found.Pilot.AddPoints(1);
            db.Set(found);
        }
        
        public static void UpdatePilotSeparateSessionsPart2(IObjectContainer db)
        {
            IObjectSet result = db.Get(new Car("Ferrari"));
            ListResult(result);
        }
        
        public static void UpdatePilotSeparateSessionsImprovedPart1(IObjectContainer db)
        {
            Db4oFactory.Configure().ObjectClass(typeof(Car))
                .CascadeOnUpdate(true);        
        }
        
        public static void UpdatePilotSeparateSessionsImprovedPart2(IObjectContainer db)
        {
            IObjectSet result = db.Get(new Car("Ferrari"));
            Car found = (Car)result.Next();
            found.Pilot.AddPoints(1);
            db.Set(found);
        }
        
        public static void UpdatePilotSeparateSessionsImprovedPart3(IObjectContainer db)
        {
            IObjectSet result = db.Get(new Car("Ferrari"));
            ListResult(result);
        }
        
        public static void DeleteFlat(IObjectContainer db)
        {
            IObjectSet 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(IObjectContainer db)
        {
            Db4oFactory.Configure().ObjectClass(typeof(Car))
                .CascadeOnDelete(true);
        }
        
        public static void DeleteDeepPart2(IObjectContainer db)
        {
            IObjectSet 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(IObjectContainer db)
        {
            IObjectSet result = db.Get(new Pilot("Michael Schumacher", 0));
            Pilot pilot = (Pilot)result.Next();
            Car car1 = new Car("Ferrari");
            Car car2 = new Car("BMW");
            car1.Pilot = pilot;
            car2.Pilot = pilot;
            db.Set(car1);
            db.Set(car2);
            db.Delete(car2);
            result = db.Get(new Car(null));
            ListResult(result);
        }
    }    
}




--
generated by
Doctor courtesy of db4objects Inc.