|
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. An example class
For this example we'll be using a hypothetical LocalizedItemList class which binds together culture information with a list of items.
System.Globalization.CultureInfo is particularly interesting because it internally holds a native pointer to a system structure which in turn cannot be cleanly stored by db4o.
Imports System.Globalization
Namespace com.db4o.f1.chapter6
''' <summary>
''' A CultureInfo aware list of objects.
''' CultureInfo objects hold a native pointer to
''' a system structure.
''' </summary>
Public Class LocalizedItemList
Private _culture As CultureInfo
Private _items As String()
Public Sub New(ByVal culture As CultureInfo, ByVal items As String())
_culture = culture
_items = items
End Sub
Public Overloads Overrides Function ToString() As String
Return String.Join(String.Concat(_culture.TextInfo.ListSeparator, " "), _items)
End Function
End Class
End Namespace
|
We'll be using this code to store and retrieve and instance of this class with different configuration settings:
Public Shared Sub TryStoreAndRetrieve()
Dim db As ObjectContainer = Db4oFactory.OpenFile(Util.YapFileName)
Try
Dim champs As String() = New String() {"Ayrton Senna", "Nelson Piquet"}
Dim LocalizedItemList As LocalizedItemList = New LocalizedItemList(CultureInfo.CreateSpecificCulture("pt-BR"), champs)
System.Console.WriteLine("ORIGINAL: {0}", LocalizedItemList)
db.[Set](LocalizedItemList)
Catch x As Exception
System.Console.WriteLine(x)
Return
Finally
db.Close()
End Try
db = Db4oFactory.OpenFile(Util.YapFileName)
Try
Dim result As ObjectSet = db.[Get](GetType(LocalizedItemList))
While result.HasNext()
Dim LocalizedItemList As LocalizedItemList = DirectCast(result.[Next](), LocalizedItemList)
System.Console.WriteLine("RETRIEVED: {0}", LocalizedItemList)
db.Delete(LocalizedItemList)
End While
Finally
db.Close()
End Try
End Sub |
12.1.1. Using the constructor
[tryStoreWithCallConstructors]
Db4oFactory.Configure().ExceptionsOnNotStorable(True)
Db4oFactory.Configure().ObjectClass(GetType(CultureInfo)).CallConstructor(True)
TryStoreAndRetrieve() |
OUTPUT: ORIGINAL: 42/Test: 4
onStore for 42/Test: 4
onStore for 42/Test: 4
onInstantiate for [Ljava.lang.Object;@8af6ec
onActivate for 42/Test: 4 / [Ljava.lang.Object;@1b24924
RETRIEVED: 42/Test: 4
|
|
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.1.2. Bypassing the constructor
[tryStoreWithoutCallConstructors]
Db4oFactory.Configure().ObjectClass(GetType(CultureInfo)).CallConstructor(False)
' trying to store objects that hold onto
' system resources can be pretty nasty
' uncomment the following line to see
' how nasty it can be
'TryStoreAndRetrieve(); |
This still does not work for our case because the native pointer will definetely be invalid. In fact this example crashes the Common Language Runtime.
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 some way to recreate its state from the information contained in the stored object, which is not the case for CultureInfo.
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 CultureInfo instances, we will store only their name since this is enough to recreate them later. Note that we don't have to do any work in onActivate(), since object reinstantiation is already fully completed in onInstantiate().
Imports System.Globalization
Imports com.db4o
Imports com.db4o.config
Namespace com.db4o.f1.chapter6
Public Class CultureInfoTranslator
Implements ObjectConstructor
Public Function OnStore(ByVal container As ObjectContainer, ByVal applicationObject As Object) As Object Implements ObjectConstructor.OnStore
System.Console.WriteLine("onStore for {0}", applicationObject)
Return (DirectCast(applicationObject, CultureInfo)).Name
End Function
Public Function OnInstantiate(ByVal container As ObjectContainer, ByVal storedObject As Object) As Object Implements ObjectConstructor.OnInstantiate
System.Console.WriteLine("onInstantiate for {0}", storedObject)
Dim name As String = DirectCast(storedObject, String)
Return CultureInfo.CreateSpecificCulture(name)
End Function
Public Sub OnActivate(ByVal container As ObjectContainer, ByVal applicationObject As Object, ByVal storedObject As Object) Implements ObjectConstructor.OnActivate
System.Console.WriteLine("onActivate for {0}/{1}", applicationObject, storedObject)
End Sub
Public Function StoredClass() As j4o.lang.Class Implements ObjectConstructor.StoredClass
Return j4o.lang.[Class].GetClassForType(GetType(String))
End Function
End Class
End Namespace
|
Let's try it out:
[storeWithTranslator]
Db4oFactory.Configure().ObjectClass(GetType(CultureInfo)).Translate(New CultureInfoTranslator())
TryStoreAndRetrieve()
Db4oFactory.Configure().ObjectClass(GetType(CultureInfo)).Translate(Nothing) |
OUTPUT: ORIGINAL: 42/Test: 4
onStore for 42/Test: 4
onStore for 42/Test: 4
onInstantiate for [Ljava.lang.Object;@4c5310
onActivate for 42/Test: 4 / [Ljava.lang.Object;@1233fdf
RETRIEVED: 42/Test: 4
|
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
Imports System
Imports System.Globalization
Imports com.db4o
Namespace com.db4o.f1.chapter6
Public Class TranslatorExample
Inherits Util
Public Shared Sub Main(ByVal args As String())
TryStoreWithCallConstructors()
TryStoreWithoutCallConstructors()
StoreWithTranslator()
End Sub
Public Shared Sub TryStoreWithCallConstructors()
Db4oFactory.Configure().ExceptionsOnNotStorable(True)
Db4oFactory.Configure().ObjectClass(GetType(CultureInfo)).CallConstructor(True)
TryStoreAndRetrieve()
End Sub
Public Shared Sub TryStoreWithoutCallConstructors()
Db4oFactory.Configure().ObjectClass(GetType(CultureInfo)).CallConstructor(False)
' trying to store objects that hold onto
' system resources can be pretty nasty
' uncomment the following line to see
' how nasty it can be
'TryStoreAndRetrieve();
End Sub
Public Shared Sub StoreWithTranslator()
Db4oFactory.Configure().ObjectClass(GetType(CultureInfo)).Translate(New CultureInfoTranslator())
TryStoreAndRetrieve()
Db4oFactory.Configure().ObjectClass(GetType(CultureInfo)).Translate(Nothing)
End Sub
Public Shared Sub TryStoreAndRetrieve()
Dim db As ObjectContainer = Db4oFactory.OpenFile(Util.YapFileName)
Try
Dim champs As String() = New String() {"Ayrton Senna", "Nelson Piquet"}
Dim LocalizedItemList As LocalizedItemList = New LocalizedItemList(CultureInfo.CreateSpecificCulture("pt-BR"), champs)
System.Console.WriteLine("ORIGINAL: {0}", LocalizedItemList)
db.[Set](LocalizedItemList)
Catch x As Exception
System.Console.WriteLine(x)
Return
Finally
db.Close()
End Try
db = Db4oFactory.OpenFile(Util.YapFileName)
Try
Dim result As ObjectSet = db.[Get](GetType(LocalizedItemList))
While result.HasNext()
Dim LocalizedItemList As LocalizedItemList = DirectCast(result.[Next](), LocalizedItemList)
System.Console.WriteLine("RETRIEVED: {0}", LocalizedItemList)
db.Delete(LocalizedItemList)
End While
Finally
db.Close()
End Try
End Sub
End Class
End Namespace
|
--
generated by Doctor courtesy of db4objects Inc.