12. Translators


In the last chapter we have covered the alternative configurations db4o offers for object reinstantiation. What's left to see is how we can store objects of a class that can't be cleanly stored with either of these approaches.


    12.1. A 'NotStorable' class


    Let's reuse our example from the previous chapter.

    package com.db4o.f1.chapter6;

    public class NotStorable {
      private int id;
      private String name;
      private transient int length;

      public NotStorable(int id,String name) {
        this.id=id;
        this.name=name;
        this.length=name.length();
      }

      public int getId() {
        return id;
      }
      
      public String getName() {
        return name;
      }  

      public int getLength() {
        return length;
      }

      public String toString() {
        return id+"/"+name+": "+length;
      }
    }


    We'll be using this code to store and retrieve and instance of this class with different configuration settings:

    public static void tryStoreAndRetrieve() {
    ObjectContainer db=Db4o.openFile(YAPFILENAME);
    try {
      NotStorable notStorable = new NotStorable(42,"Test");
      System.out.println("ORIGINAL: "+notStorable);
      db.set(notStorable);
    }
    catch(Exception exc) {
      System.out.println(exc.toString());
      return;
    }
    finally {
      db.close();
    }
    db=Db4o.openFile(YAPFILENAME);
    try {
      ObjectSet result=db.get(NotStorable.class);
      while(result.hasNext()) {
        NotStorable notStorable=(NotStorable)result.next();
        System.out.println("RETRIEVED: "+notStorable);
        db.delete(notStorable);
      }
    }
    finally {
      db.close();
    }
      }



      12.1.1. Using the constructor



      12.1.2. Bypassing the constructor


      [tryStoreWithoutCallConstructors]
      Db4o.configure().exceptionsOnNotStorable(false);
      Db4o.configure().objectClass(NotStorable.class)
          .callConstructor(false);
      tryStoreAndRetrieve();


      In this case our object seems to be nicely stored and retrieved, however, it has forgotten about its length, since db4o doesn't store transient members and the constructor code that sets it is not executed.

      [tryStoreWithCallConstructors]
      Db4o.configure().exceptionsOnNotStorable(true);
          Db4o.configure().objectClass(NotStorable.class)
      .callConstructor(true);
          tryStoreAndRetrieve();


      At storage time, db4o tests the only available constructor with null arguments and runs into a NullPointerException, so it refuses to accept our object.

      (Note that this test only occurs when configured with exceptionsOnNotStorable - otherwise db4o will silently fail when trying to reinstantiate the object.)


    12.2. The Translator API


    So how do we get our object into the database, now that everything seems to fail? Db4o provides a way to specify a custom way of storing and retrieving objects through the ObjectTranslator and ObjectConstructor interfaces.


      12.2.1. ObjectTranslator


      The ObjectTranslator API looks like this:

      public Object onStore(ObjectContainer container,
                            Object applicationObject);
      public void onActivate(ObjectContainer container,
                             Object applicationObject,
                             Object storedObject);
      public Class storedClass ();


      The usage is quite simple: When a translator is configured for a class, db4o will call its onStore method with a reference to the database and the instance to be stored as a parameter and will store the object returned. This object's type has to be primitive from a db4o point of view and it has to match the type specification returned by storedClass().

      On retrieval, db4o will create a blank object of the target class (using the configured instantiation method) and then pass it on to onActivate() along with the stored object to be set up accordingly.


      12.2.2. ObjectConstructor


      However, this will only work if the application object's class provides sufficient setters to recreate its state from the information contained in the stored object, which is not the case for our example class.

      For these cases db4o provides an extension to the ObjectTranslator interface, ObjectConstructor, which declares one additional method:

      public Object onInstantiate(ObjectContainer container,
                                  Object storedObject);


      If db4o detects a configured translator to be an ObjectConstructor implementation, it will pass the stored class instance to the onInstantiate() method and use the result as a blank application object to be processed by onActivate().

      Note that, while in general configured translators are applied to subclasses, too, ObjectConstructor application object instantiation will not be used for subclasses (which wouldn't make much sense, anyway), so ObjectConstructors have to be configured for the concrete classes.


    12.3. A translator implementation


    To translate NotStorable instances, we will pack their id and name values into an Object array to be stored and retrieve it from there again. Note that we don't have to do any work in onActivate(), since object reinstantiation is already fully completed in onInstantiate().

    package com.db4o.f1.chapter6;

    import com.db4o.*;
    import com.db4o.config.*;

    public class NotStorableTranslator
        implements ObjectConstructor {
      public Object onStore(ObjectContainer container,
          Object applicationObject) {
        System.out.println("onStore for "+applicationObject);
        NotStorable notStorable=(NotStorable)applicationObject;
        return new Object[]{new Integer(notStorable.getId()),
            notStorable.getName()};
      }

      public Object onInstantiate(ObjectContainer container,
          Object storedObject) {
        System.out.println("onInstantiate for "+storedObject);
        Object[] raw=(Object[])storedObject;
        int id=((Integer)raw[0]).intValue();
        String name=(String)raw[1];
        return new NotStorable(id,name);
      }

      public void onActivate(ObjectContainer container,
          Object applicationObject, Object storedObject) {
        System.out.println("onActivate for "+applicationObject
            +" / "+storedObject);
      }

      public Class storedClass() {
        return Object[].class;
      }
    }


    Let's try it out:

    [storeWithTranslator]
    Db4o.configure().objectClass(NotStorable.class)
    .translate(new NotStorableTranslator());
        tryStoreAndRetrieve();



    12.4. Conclusion


    For classes that cannot cleanly be stored and retrieved with db4o's standard object instantiation mechanisms, db4o provides an API to specify custom reinstantiation strategies. These also come in two flavors: ObjectTranslators let you reconfigure the state of a 'blank' application object reinstantiated by db4o, ObjectConstructors also take care of instantiating the application object itself.


    12.5. Full source


    package com.db4o.f1.chapter6;

    import com.db4o.*;
    import com.db4o.f1.*;

    public class TranslatorExample extends Util {
      public static void main(String[] args) {
        tryStoreWithoutCallConstructors();
        tryStoreWithCallConstructors();
        storeWithTranslator();
      }

      public static void tryStoreWithoutCallConstructors() {
            Db4o.configure().exceptionsOnNotStorable(false);
            Db4o.configure().objectClass(NotStorable.class)
                .callConstructor(false);
            tryStoreAndRetrieve();
      }

      public static void tryStoreWithCallConstructors() {
        Db4o.configure().exceptionsOnNotStorable(true);
        Db4o.configure().objectClass(NotStorable.class)
            .callConstructor(true);
        tryStoreAndRetrieve();
      }

      public static void storeWithTranslator() {
        Db4o.configure().objectClass(NotStorable.class)
            .translate(new NotStorableTranslator());
        tryStoreAndRetrieve();
      }

      public static void tryStoreAndRetrieve() {
        ObjectContainer db=Db4o.openFile(YAPFILENAME);
        try {
          NotStorable notStorable = new NotStorable(42,"Test");
          System.out.println("ORIGINAL: "+notStorable);
          db.set(notStorable);
        }
        catch(Exception exc) {
          System.out.println(exc.toString());
          return;
        }
        finally {
          db.close();
        }
        db=Db4o.openFile(YAPFILENAME);
        try {
          ObjectSet result=db.get(NotStorable.class);
          while(result.hasNext()) {
            NotStorable notStorable=(NotStorable)result.next();
            System.out.println("RETRIEVED: "+notStorable);
            db.delete(notStorable);
          }
        }
        finally {
          db.close();
        }
      }
    }



    --
    generated by
    Doctor courtesy of db4objects Inc.