You are here: Basic Concepts > Object Identity > Unique Identity Concept

Unique identity concept

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.

IdentityExample.java: checkUniqueness
private static void checkUniqueness() {
    setObjects();
    ObjectContainer container = Db4o.openFile(DB4O_FILE_NAME);
    try  {
      ObjectSet cars = container.query(Car.class);
      Car car = (Car)cars.queryByExample(0);
      String pilotName = car.getPilot().getName();
      ObjectSet pilots = container.queryByExample(new Pilot(pilotName));
      Pilot pilot = (Pilot)pilots.queryByExample(0);
      System.out.println("Retrieved objects are identical: " + (pilot == car.getPilot()));
    } finally  {
      container.close();
    }
  }

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

IdentityExample.java: checkReferenceCache
private static void checkReferenceCache() {
    setObjects();
    ObjectContainer container = Db4o.openFile(DB4O_FILE_NAME);
    try  {
      ObjectSet pilots = container.query(Pilot.class);
      Pilot pilot = (Pilot)pilots.queryByExample(0);
      String pilotName = pilot.getName();
      pilot.setName("new name");
      System.out.println("Retrieving pilot by name: " + pilotName);
      ObjectSet pilots1 = container.queryByExample(new Pilot(pilotName));
      listResult(pilots1);
    } finally  {
      container.close();
    }
  }

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:

IdentityExample.java: checkReferenceCacheWithPurge
private static void checkReferenceCacheWithPurge() {
    setObjects();
    ObjectContainer container = Db4o.openFile(DB4O_FILE_NAME);
    try  {
      ObjectSet pilots = container.query(Pilot.class);
      Pilot pilot = (Pilot)pilots.queryByExample(0);
      String pilotName = pilot.getName();
      pilot.setName("new name");
      System.out.println("Retrieving pilot by name: " + pilotName);
      long pilotID = container.ext().getID(pilot);
      if (container.ext().isCached(pilotID)) {
        container.ext().purge(pilot);
      }
      ObjectSet pilots1 = container.queryByExample(new Pilot(pilotName));
      listResult(pilots1);
    } finally  {
      container.close();
    }
  }

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:

IdentityExample.java: testCopyingWithPurge
private static void testCopyingWithPurge() {
    setObjects();
    ObjectContainer container = Db4o.openFile(DB4O_FILE_NAME);
    try  {
      ObjectSet pilots = container.query(Pilot.class);
      Pilot pilot = (Pilot)pilots.queryByExample(0);
      container.ext().purge(pilot);
      container.store(pilot);
      pilots = container.query(Pilot.class);
      listResult(pilots);
    } finally  {
      container.close();
    }
  }

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.

Actually db4o reference is a pointer to the object in the database file. It means that the size of the database does not affect query time: the object is retrieved from the known position without any necessity to traverse values.

Download example code:

Java