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 com.db4o.f1.chapter2
    Public Class Car
        Private _model As String
        Private _pilot As Pilot
        Public Sub New(ByVal model As String)
            _model = model
            _pilot = Nothing
        End Sub
        Public Property Pilot() As Pilot
            Get
                Return _pilot
            End Get
            Set
                _pilot = value
            End Set
        End Property
        Public ReadOnly Property Model() As String
            Get
                Return _model
            End Get
        End Property
        Public Overloads Overrides Function ToString() As String
            Return String.Format("{0}[{1}]", _model, _pilot)
        End Function
    End Class
End Namespace



    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]
    Dim car1 As Car = New Car("Ferrari")
    Dim pilot1 As Pilot = 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]
    Dim pilot2 As Pilot = New Pilot("Rubens Barrichello", 99)
    db.[Set](pilot2)
    Dim car2 As Car = 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]
      Dim proto As Car = New Car(Nothing)
      Dim result As ObjectSet = 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]
Dim proto As Pilot = New Pilot(Nothing, 0)
Dim result As ObjectSet = 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]
Dim pilotproto As Pilot = New Pilot("Rubens Barrichello", 0)
Dim carproto As Car = New Car(Nothing)
carproto.Pilot = pilotproto
Dim result As ObjectSet = 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:

Public Class RetrieveCarsByPilotNamePredicate
Inherits Predicate
    ReadOnly _pilotName As String
    Public Sub New(ByVal pilotName As String)
        _pilotName = pilotName
    End Sub

[retrieveCarsByPilotNameNative]
Dim pilotName As String = "Rubens Barrichello"
Dim results As ObjectSet = db.Query(New RetrieveCarsByPilotNamePredicate(pilotName))
ListResult(results)
OUTPUT:
1
BMW[Rubens Barrichello/99]


Using .NET 2.0 syntax this is a lot simpler:

C# .NET 2.0
[retrieveCarsByPilotNameNative]
string pilotName = "Rubens Barrichello";
List<Car> results = db.Query<Car>(delegate(Car car) {
    return car.Pilot.Name == pilotName; });
listResults(results);




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]
Dim query As Query = db.Query()
query.Constrain(GetType(Car))
query.Descend("_pilot").Descend("_name").Constrain("Rubens Barrichello")
Dim result As ObjectSet = 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]
Dim query As Query = db.Query()
query.Constrain(GetType(Car))
Dim proto As Pilot = New Pilot("Rubens Barrichello", 0)
query.Descend("_pilot").Constrain(proto)
Dim result As ObjectSet = 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]
Dim carQuery As Query = db.Query()
carQuery.Constrain(GetType(Car))
carQuery.Descend("_model").Constrain("Ferrari")
Dim pilotQuery As Query = carQuery.Descend("_pilot")
Dim result As ObjectSet = 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]
Dim result As ObjectSet = db.[Get](New Car("Ferrari"))
Dim found As Car = DirectCast(result.[Next](), Car)
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]
Dim result As ObjectSet = db.[Get](New Car("Ferrari"))
Dim found As Car = DirectCast(result.[Next](), Car)
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]
Dim result As ObjectSet = db.[Get](New Car("Ferrari"))
Dim found As Car = DirectCast(result.[Next](), Car)
found.Pilot.AddPoints(1)
db.[Set](found)


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

[updatePilotSeparateSessionsPart2]
Dim result As ObjectSet = 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]
Dim result As ObjectSet = db.[Get](New Car("Ferrari"))
Dim found As Car = DirectCast(result.[Next](), Car)
db.Delete(found)
result = db.[Get](New Car(Nothing))
ListResult(result)
OUTPUT:
1
BMW[Rubens Barrichello/99]


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

[retrieveAllPilotsQBE]
Dim proto As Pilot = New Pilot(Nothing, 0)
Dim result As ObjectSet = 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


Imports System
Imports System.IO
Imports com.db4o
Imports com.db4o.query
Namespace com.db4o.f1.chapter2
    Public Class StructuredExample
    Inherits Util
        Public Shared Sub Main(ByVal args As String())
            File.Delete(Util.YapFileName)
            Dim db As ObjectContainer = 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()
            End Try
        End Sub
        Public Shared Sub StoreFirstCar(ByVal db As ObjectContainer)
            Dim car1 As Car = New Car("Ferrari")
            Dim pilot1 As Pilot = New Pilot("Michael Schumacher", 100)
            car1.Pilot = pilot1
            db.[Set](car1)
        End Sub
        Public Shared Sub StoreSecondCar(ByVal db As ObjectContainer)
            Dim pilot2 As Pilot = New Pilot("Rubens Barrichello", 99)
            db.[Set](pilot2)
            Dim car2 As Car = New Car("BMW")
            car2.Pilot = pilot2
            db.[Set](car2)
        End Sub
        Public Shared Sub RetrieveAllCarsQBE(ByVal db As ObjectContainer)
            Dim proto As Car = New Car(Nothing)
            Dim result As ObjectSet = db.[Get](proto)
            ListResult(result)
        End Sub
        Public Shared Sub RetrieveAllPilotsQBE(ByVal db As ObjectContainer)
            Dim proto As Pilot = New Pilot(Nothing, 0)
            Dim result As ObjectSet = db.[Get](proto)
            ListResult(result)
        End Sub
        Public Shared Sub RetrieveCarByPilotQBE(ByVal db As ObjectContainer)
            Dim pilotproto As Pilot = New Pilot("Rubens Barrichello", 0)
            Dim carproto As Car = New Car(Nothing)
            carproto.Pilot = pilotproto
            Dim result As ObjectSet = db.[Get](carproto)
            ListResult(result)
        End Sub
        Public Shared Sub RetrieveCarByPilotNameQuery(ByVal db As ObjectContainer)
            Dim query As Query = db.Query()
            query.Constrain(GetType(Car))
            query.Descend("_pilot").Descend("_name").Constrain("Rubens Barrichello")
            Dim result As ObjectSet = query.Execute()
            ListResult(result)
        End Sub
        Public Shared Sub RetrieveCarByPilotProtoQuery(ByVal db As ObjectContainer)
            Dim query As Query = db.Query()
            query.Constrain(GetType(Car))
            Dim proto As Pilot = New Pilot("Rubens Barrichello", 0)
            query.Descend("_pilot").Constrain(proto)
            Dim result As ObjectSet = query.Execute()
            ListResult(result)
        End Sub
        Public Shared Sub RetrievePilotByCarModelQuery(ByVal db As ObjectContainer)
            Dim carQuery As Query = db.Query()
            carQuery.Constrain(GetType(Car))
            carQuery.Descend("_model").Constrain("Ferrari")
            Dim pilotQuery As Query = carQuery.Descend("_pilot")
            Dim result As ObjectSet = pilotQuery.Execute()
            ListResult(result)
        End Sub
        Public Shared Sub RetrieveAllPilots(ByVal db As ObjectContainer)
            Dim results As ObjectSet = db.[Get](GetType(Pilot))
            ListResult(results)
        End Sub
        Public Shared Sub RetrieveAllCars(ByVal db As ObjectContainer)
            Dim results As ObjectSet = db.[Get](GetType(Car))
            ListResult(results)
        End Sub
        Public Class RetrieveCarsByPilotNamePredicate
        Inherits Predicate
            ReadOnly _pilotName As String
            Public Sub New(ByVal pilotName As String)
                _pilotName = pilotName
            End Sub
            Public Function Match(ByVal candidate As Car) As Boolean
                Return candidate.Pilot.Name = _pilotName
            End Function
        End Class
        Public Shared Sub RetrieveCarsByPilotNameNative(ByVal db As ObjectContainer)
            Dim pilotName As String = "Rubens Barrichello"
            Dim results As ObjectSet = db.Query(New RetrieveCarsByPilotNamePredicate(pilotName))
            ListResult(results)
        End Sub
        Public Shared Sub UpdateCar(ByVal db As ObjectContainer)
            Dim result As ObjectSet = db.[Get](New Car("Ferrari"))
            Dim found As Car = DirectCast(result.[Next](), Car)
            found.Pilot = New Pilot("Somebody else", 0)
            db.[Set](found)
            result = db.[Get](New Car("Ferrari"))
            ListResult(result)
        End Sub
        Public Shared Sub UpdatePilotSingleSession(ByVal db As ObjectContainer)
            Dim result As ObjectSet = db.[Get](New Car("Ferrari"))
            Dim found As Car = DirectCast(result.[Next](), Car)
            found.Pilot.AddPoints(1)
            db.[Set](found)
            result = db.[Get](New Car("Ferrari"))
            ListResult(result)
        End Sub
        Public Shared Sub UpdatePilotSeparateSessionsPart1(ByVal db As ObjectContainer)
            Dim result As ObjectSet = db.[Get](New Car("Ferrari"))
            Dim found As Car = DirectCast(result.[Next](), Car)
            found.Pilot.AddPoints(1)
            db.[Set](found)
        End Sub
        Public Shared Sub UpdatePilotSeparateSessionsPart2(ByVal db As ObjectContainer)
            Dim result As ObjectSet = db.[Get](New Car("Ferrari"))
            ListResult(result)
        End Sub
        Public Shared Sub UpdatePilotSeparateSessionsImprovedPart1(ByVal db As ObjectContainer)
            Db4oFactory.Configure().ObjectClass(GetType(Car)).CascadeOnUpdate(True)
        End Sub
        Public Shared Sub UpdatePilotSeparateSessionsImprovedPart2(ByVal db As ObjectContainer)
            Dim result As ObjectSet = db.[Get](New Car("Ferrari"))
            Dim found As Car = DirectCast(result.[Next](), Car)
            found.Pilot.AddPoints(1)
            db.[Set](found)
        End Sub
        Public Shared Sub UpdatePilotSeparateSessionsImprovedPart3(ByVal db As ObjectContainer)
            Dim result As ObjectSet = db.[Get](New Car("Ferrari"))
            ListResult(result)
        End Sub
        Public Shared Sub DeleteFlat(ByVal db As ObjectContainer)
            Dim result As ObjectSet = db.[Get](New Car("Ferrari"))
            Dim found As Car = DirectCast(result.[Next](), Car)
            db.Delete(found)
            result = db.[Get](New Car(Nothing))
            ListResult(result)
        End Sub
        Public Shared Sub DeleteDeepPart1(ByVal db As ObjectContainer)
            Db4oFactory.Configure().ObjectClass(GetType(Car)).CascadeOnDelete(True)
        End Sub
        Public Shared Sub DeleteDeepPart2(ByVal db As ObjectContainer)
            Dim result As ObjectSet = db.[Get](New Car("BMW"))
            Dim found As Car = DirectCast(result.[Next](), Car)
            db.Delete(found)
            result = db.[Get](New Car(Nothing))
            ListResult(result)
        End Sub
        Public Shared Sub DeleteDeepRevisited(ByVal db As ObjectContainer)
            Dim result As ObjectSet = db.[Get](New Pilot("Michael Schumacher", 0))
            Dim pilot As Pilot = DirectCast(result.[Next](), Pilot)
            Dim car1 As Car = New Car("Ferrari")
            Dim car2 As Car = New Car("BMW")
            car1.Pilot = pilot
            car2.Pilot = pilot
            db.[Set](car1)
            db.[Set](car2)
            db.Delete(car2)
            result = db.[Get](New Car(Nothing))
            ListResult(result)
        End Sub
    End Class
End Namespace




--
generated by
Doctor courtesy of db4objects Inc.