Db4o uses the concept of uniqueness of each object in reference cache. If an object is accessed by multiple queries or through multiple navigation access paths, db4o will always return the one single object, helping you to put your object graph together exactly the same way as it was when it was stored, without having to use IDs. You can simply use '==' to check the identity of two database objects.
01private static void CheckUniqueness() 02
{ 03
SetObjects(); 04
IObjectContainer db = Db4oFactory.OpenFile(Db4oFileName); 05
try 06
{ 07
IObjectSet cars = db.Get(typeof(Car)); 08
Car car = (Car)cars[0]; 09
String pilotName = car.Pilot.Name; 10
IObjectSet pilots = db.Get(new Pilot(pilotName)); 11
Pilot pilot = (Pilot)pilots[0]; 12
System.Console.WriteLine("Retrieved objects are identical: " + (pilot == car.Pilot)); 13
} 14
finally 15
{ 16
db.Close(); 17
} 18
}
01Private Shared Sub CheckUniqueness() 02
SetObjects() 03
Dim db As IObjectContainer = Db4oFactory.OpenFile(Db4oFileName) 04
Try 05
Dim cars As IObjectSet = db.Get(GetType(Car)) 06
Dim car As Car = CType(cars(0), Car) 07
Dim pilotName As String = car.Pilot.Name 08
Dim pilots As IObjectSet = db.Get(New Pilot(pilotName)) 09
Dim pilot As Pilot = CType(pilots(0), Pilot) 10
System.Console.WriteLine("Retrieved objects are identical: " + (pilot Is car.Pilot).ToString()) 11
Finally 12
db.Close() 13
End Try 14
End Sub
How does db4o realize such behavior? Each object is loaded into reference cache only once in the session: db4o will return a new object only if it is not present in the cache yet, otherwise it will give you a reference to the object already in cache. This helps db4o to distinguish between objects that are to be updated and those ones that are to be created. All "known" objects are the subjects of update whereas "unknown" should be created. (Note that the reference system will only be in place as long as an ObjectContainer is open. Closing and reopening an ObjectContainer will clean the references system of the ObjectContainer and all objects in RAM will be treated as "new" afterwards.).
01private static void CheckReferenceCache() 02
{ 03
SetObjects(); 04
IObjectContainer db = Db4oFactory.OpenFile(Db4oFileName); 05
try 06
{ 07
IObjectSet pilots = db.Get(typeof(Pilot)); 08
Pilot pilot = (Pilot)pilots[0]; 09
String pilotName = pilot.Name; 10
pilot.Name = "new name"; 11
System.Console.WriteLine("Retrieving pilot by name: " + pilotName); 12
IObjectSet pilots1 = db.Get(new Pilot(pilotName)); 13
ListResult(pilots1); 14
} 15
finally 16
{ 17
db.Close(); 18
} 19
}
01Private Shared Sub CheckReferenceCache() 02
SetObjects() 03
Dim db As IObjectContainer = Db4oFactory.OpenFile(Db4oFileName) 04
Try 05
Dim pilots As IObjectSet = db.Get(GetType(Pilot)) 06
Dim pilot As Pilot = CType(pilots(0), Pilot) 07
Dim pilotName As String = pilot.Name 08
pilot.Name = "new name" 09
System.Console.WriteLine("Retrieving pilot by name: " + pilotName) 10
Dim pilots1 As IObjectSet = db.Get(New Pilot(pilotName)) 11
ListResult(pilots1) 12
Finally 13
db.Close() 14
End Try 15
End Sub
In the example pilot object is retrieved from the database (placed into cache) and changed, but not saved. The following retrieval uses pilot's name to retrieve the object from the database, but that object was already instantiated, so its cached (and modified) instance is actually returned.
Such behavior can be sometimes undesirable - you may expect to get object as it saved in the database instead of its modified instance in cache. One of the ways to do that is to use ExtObjectContainer#peekPersisted(object) method, which will give you a disconnected copy of a database object.
Another way is to purge objects from the cache before re-retrieving them.
You can use the following methods:
Let's look at our previous example extended with these methods:
01private static void CheckReferenceCacheWithPurge() 02
{ 03
SetObjects(); 04
IObjectContainer db = Db4oFactory.OpenFile(Db4oFileName); 05
try 06
{ 07
IObjectSet pilots = db.Get(typeof(Pilot)); 08
Pilot pilot = (Pilot)pilots[0]; 09
String pilotName = pilot.Name; 10
pilot.Name = "new name"; 11
System.Console.WriteLine("Retrieving pilot by name: " + pilotName); 12
long pilotID = db.Ext().GetID(pilot); 13
if (db.Ext().IsCached(pilotID)) 14
{ 15
db.Ext().Purge(pilot); 16
} 17
IObjectSet pilots1 = db.Get(new Pilot(pilotName)); 18
ListResult(pilots1); 19
} 20
finally 21
{ 22
db.Close(); 23
} 24
}
01Private Shared Sub CheckReferenceCacheWithPurge() 02
SetObjects() 03
Dim db As IObjectContainer = Db4oFactory.OpenFile(Db4oFileName) 04
Try 05
Dim pilots As IObjectSet = db.Get(GetType(Pilot)) 06
Dim pilot As Pilot = CType(pilots(0), Pilot) 07
Dim pilotName As String = pilot.Name 08
pilot.Name = "new name" 09
System.Console.WriteLine("Retrieving pilot by name: " + pilotName) 10
Dim pilotID As Long = db.Ext().GetID(pilot) 11
If db.Ext().IsCached(pilotID) Then 12
db.Ext().Purge(pilot) 13
End If 14
Dim pilots1 As IObjectSet = db.Get(New Pilot(pilotName)) 15
ListResult(pilots1) 16
Finally 17
db.Close() 18
End Try 19
End Sub
Now the second retrieval re-instantiates Pilot object from the database.
An object removed with ExtObjectContainer#purge(object) becomes "unknown" to the ObjectContainer, so this method may also be used to create multiple copies of objects:
01private static void TestCopyingWithPurge() 02
{ 03
SetObjects(); 04
IObjectContainer db = Db4oFactory.OpenFile(Db4oFileName); 05
try 06
{ 07
IObjectSet pilots = db.Get(typeof(Pilot)); 08
Pilot pilot = (Pilot)pilots[0]; 09
db.Ext().Purge(pilot); 10
db.Set(pilot); 11
pilots = db.Get(typeof(Pilot)); 12
ListResult(pilots); 13
} 14
finally 15
{ 16
db.Close(); 17
} 18
}
01Private Shared Sub TestCopyingWithPurge() 02
SetObjects() 03
Dim db As IObjectContainer = Db4oFactory.OpenFile(Db4oFileName) 04
Try 05
Dim pilots As IObjectSet = db.Get(GetType(Pilot)) 06
Dim pilot As Pilot = CType(pilots(0), Pilot) 07
db.Ext().Purge(pilot) 08
db.Set(pilot) 09
pilots = db.Get(GetType(Pilot)) 10
ListResult(pilots) 11
Finally 12
db.Close() 13
End Try 14
End Sub
Each reference in db4o works directly with the object. As only one instance of the object exists in cache there is no problem with object locks.
You can see an example of another concept used in JDO system http://access1.sun.com/jdo/
Actually db4o reference is a pointer to the object in the database file. It means that the size of the database does not effect query time: the object is retrieved from the known position without any necessity to traverse values.