<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <link rel="stylesheet" type="text/css" href="docs.css"> <!--[if gte IE 5]> <link href="docs_ie.css" rel="stylesheet" type="text/css"> <![endif]--> </head> <body><div id="pagecontainer"><table><tr><td width="5"> </td><td><a name="TransparentPersistence"></a><br> <a name="outline198"></a><br><h1>10. Transparent Persistence</h1><br> The problem of updating deep object structures was briefly outlined in <a href="Structured.html#Structured">Structured objects chapter</a> . Update Depth configuration setting gives a user a certain control over the object updating and allows to find a balance between performance and convenient object storage code. However, this balance is far from ideal:<br> - when update depth is zero, each piece of code should "know" how many levels of objects should be updated; this potentially creates lots of problems when the objects are refactored;<br> - when update depth is maximum performance can become very poor as many unchanged objects will be stored unnecessary.<br> <br> The solution to this problem is to let db4o engine decide, which objects were modified and should be stored. This feature was introduced in db4o version 7.1 and was named Transparent Persistence. So how does it work?<br> <br> 1. Database should be configured to use TransparentPersistenceSupport.<br> 2. Persistent classes available for Transparent Persistence must implement interface. This interface provides a bind method to bind an object to the object container's activator.<br> 3. The object is bound to the object container when it is first stored or instantiated from the database.<br> 4. When an object field is modified in the runtime, <em>Activate()</em> method is called to register the object to be stored with the next commit. The difference from <a href="TransparentActivation.html#TransparentActivation">Transparent Activation</a> is in the activation purpose: <em>ActivationPurpose.Write</em> is used for TP.<br> 5. When the transaction is committed or the database is closed, db4o traverses the list of modified 'Activatable' objects and persists them.<br> Well, that's enough of theory, let's look at an example.<br> <br> <ul> <a name="outline199"></a><br><h2>10.1. Transparent Persistence Example</h2> <br> We will use Car and SensorReadout classes from <a href="Deep.html#Deep">Deep Graphs chapter</a>. These are persistent classes, so if we want to use Transparent Persistence, we should make them "visible" to Transparent Persistence by implementing <em>IActivatable</em> interface.<br> <br> <table width="100%" cellpadding="3" cellspacing="0" border="0"><tr><td class="lg"> <code>using System;<br> using Db4objects.Db4o.Activation;<br> using Db4objects.Db4o.TA;<br> namespace Db4objects.Db4o.Tutorial.F1.Chapter8<br> {<br> public class Car : IActivatable <br> {<br> private readonly String _model;<br> private SensorReadout _history;<br> <br> [Transient]<br> private IActivator _activator;<br> public Car(String model) <br> {<br> this._model=model;<br> this._history=null;<br> }<br> public String Model<br> {<br> get<br> {<br> Activate(ActivationPurpose.Read);<br> return _model;<br> }<br> }<br> <br> public SensorReadout History<br> {<br> get<br> {<br> Activate(ActivationPurpose.Read);<br> return _history;<br> }<br> }<br> <br> public void snapshot() <br> { <br> AppendToHistory(new TemperatureSensorReadout(DateTime.Now,this,"oil", PollOilTemperature()));<br> AppendToHistory(new TemperatureSensorReadout(DateTime.Now, this, "water", PollWaterTemperature()));<br> }<br> protected double PollOilTemperature() <br> {<br> return 0.1* CountHistoryElements();<br> }<br> protected double PollWaterTemperature() <br> {<br> return 0.2* CountHistoryElements();<br> }<br> public override String ToString() <br> {<br> Activate(ActivationPurpose.Read);<br> return string.Format("{0}/{1}", _model, CountHistoryElements());<br> }<br> <br> private int CountHistoryElements() <br> {<br> Activate(ActivationPurpose.Read);<br> return (_history==null ? 0 : _history.CountElements());<br> }<br> <br> private void AppendToHistory(SensorReadout readout) <br> {<br> Activate(ActivationPurpose.Write);<br> if(_history==null) <br> {<br> _history=readout;<br> }<br> else <br> {<br> _history.Append(readout);<br> }<br> }<br> public void Activate(ActivationPurpose purpose) <br> {<br> if(_activator != null) <br> {<br> _activator.Activate(purpose);<br> }<br> }<br> public void Bind(IActivator activator) <br> {<br> if (_activator == activator)<br> {<br> return;<br> }<br> if (activator != null && null != _activator)<br> {<br> throw new System.InvalidOperationException();<br> }<br> _activator = activator;<br> }<br> }<br> }<br> </code></td></tr></table> <br> <br> Note, that we've added an <em>_activator</em> field, <em>Bind</em> and <em>Activate</em> methods to implement <em>IActivatable</em> interface. In addition to that all methods that read or write object fields has got <em>activate</em> calls with a corresponding purpose.<br> Similar modifications should be done to the SensorReadout class.<br> Now we are ready to test how Transparent Persistence work. First we should configure the database to use TransparentPersistenceSupport:<br> <table width="100%" cellpadding="3" cellspacing="0" border="0"><tr><td class="lg"> <code>// configureTransparentPersistence<br> Db4oFactory.Configure().Add(new TransparentPersistenceSupport());</code></td><td class="lg" align="left" valign="bottom" width=43><input type='button' class='button' value='Run' onclick='window.external.RunExample("com.db4o.f1.chapter8.TransparentPersistenceExample", "configureTransparentPersistence")' /></td></tr></table> <br> Now we can store some objects: <br> <table width="100%" cellpadding="3" cellspacing="0" border="0"><tr><td class="lg"> <code>// storeCarAndSnapshots<br> Car car = new Car("Ferrari");<br> for (int i = 0; i < 3; i++)<br> {<br> car.snapshot();<br> }<br> db.Store(car);</code></td><td class="lg" align="left" valign="bottom" width=43><input type='button' class='button' value='Run' onclick='window.external.RunExample("com.db4o.f1.chapter8.TransparentPersistenceExample", "storeCarAndSnapshots")' /></td></tr></table> <br> Ok, all the objects are stored. <br> Now, let's retrieve all the stored objects and modify them:<br> <table width="100%" cellpadding="3" cellspacing="0" border="0"><tr><td class="lg"> <code>// modifySnapshotHistory<br> System.Console.WriteLine("Read all sensors and modify the description:");<br> IObjectSet result=db.QueryByExample(typeof(Car));<br> Car car=(Car)result.Next();<br> SensorReadout readout=car.History;<br> while(readout!=null) {<br> System.Console.WriteLine(readout);<br> readout.Description = "Modified: " + readout.Description;<br> readout = readout.Next;<br> }<br> db.Commit();</code></td><td class="lg" align="left" valign="bottom" width=43><input type='button' class='button' value='Run' onclick='window.external.RunExample("com.db4o.f1.chapter8.TransparentPersistenceExample", "modifySnapshotHistory")' /></td></tr></table> <br> You can see that we do not have to call <em>#store</em> any more - all the objects are stored when <em>#commit</em> is called.<br> Let's test that the modifications actually reached the database:<br> <table width="100%" cellpadding="3" cellspacing="0" border="0"><tr><td class="lg"> <code>// readSnapshotHistory<br> System.Console.WriteLine("Read all modified sensors:");<br> IObjectSet result=db.QueryByExample(typeof(Car));<br> Car car=(Car)result.Next();<br> SensorReadout readout=car.History;<br> while(readout!=null) {<br> System.Console.WriteLine(readout);<br> readout=readout.Next;<br> }</code></td><td class="lg" align="left" valign="bottom" width=43><input type='button' class='button' value='Run' onclick='window.external.RunExample("com.db4o.f1.chapter8.TransparentPersistenceExample", "readSnapshotHistory")' /></td></tr></table> <br> Yes, it is all as it should be. If you want to see the difference without Transparent Persistence, run the same examples without calling <em>configureTransparentPersistence</em> .<br> <br> <a name="outline200"></a><br><h2>10.2. Transparent Persistence Enhancement</h2>As we saw <a href="TransparentActivation.html#TransparentActivation">before</a> enhancement tools can simplify the process for Transparent Activation. The same applies to Transparent Persistence. Actually Transparent Persistence enhancement implicitly provides TA for enhanced classes. <br> For more information please refer to <a href="Enhancement.html#Enhancement">Enhancement chapter</a> .<br> <br> <a name="outline201"></a><br><h2>10.3. Conclusion</h2>Transparent Persistence can considerably simplify the development process at the same time providing considerable performance benefits. For more information on Transparent Persistence please refer to our <a href="http://developer.db4o.com/Resources/view.aspx/Reference/Object_Lifecycle/Transparent_Persistence" target="_blank">online reference</a> or your offline copy of the Reference documentation. <br> <br> <a name="outline202"></a><br><h2>10.4. Full source</h2><br> <table width="100%" cellpadding="3" cellspacing="0" border="0"><tr><td class="lg"> <code>using System;<br> using System.IO;<br> using Db4objects.Db4o;<br> using Db4objects.Db4o.TA;<br> using Db4objects.Db4o.Tutorial.F1;<br> namespace Db4objects.Db4o.Tutorial.F1.Chapter8<br> {<br> public class TransparentPersistenceExample : Util<br> {<br> public static void Main(String[] args)<br> {<br> File.Delete(YapFileName);<br> ConfigureTransparentPersistence();<br> IObjectContainer db = Db4oFactory.OpenFile(YapFileName);<br> try<br> {<br> StoreCarAndSnapshots(db);<br> db.Close();<br> db = Db4oFactory.OpenFile(YapFileName);<br> ModifySnapshotHistory(db);<br> db.Close();<br> db = Db4oFactory.OpenFile(YapFileName);<br> ReadSnapshotHistory(db);<br> }<br> finally<br> {<br> db.Close();<br> }<br> }<br> public static void ConfigureTransparentPersistence()<br> {<br> Db4oFactory.Configure().Add(new TransparentPersistenceSupport());<br> }<br> public static void StoreCarAndSnapshots(IObjectContainer db)<br> {<br> Car car = new Car("Ferrari");<br> for (int i = 0; i < 3; i++)<br> {<br> car.snapshot();<br> }<br> db.Store(car);<br> }<br> public static void ModifySnapshotHistory(IObjectContainer db) {<br> System.Console.WriteLine("Read all sensors and modify the description:");<br> IObjectSet result=db.QueryByExample(typeof(Car));<br> Car car=(Car)result.Next();<br> SensorReadout readout=car.History;<br> while(readout!=null) {<br> System.Console.WriteLine(readout);<br> readout.Description = "Modified: " + readout.Description;<br> readout = readout.Next;<br> }<br> db.Commit();<br> }<br> public static void ReadSnapshotHistory(IObjectContainer db) {<br> System.Console.WriteLine("Read all modified sensors:");<br> IObjectSet result=db.QueryByExample(typeof(Car));<br> Car car=(Car)result.Next();<br> SensorReadout readout=car.History;<br> while(readout!=null) {<br> System.Console.WriteLine(readout);<br> readout=readout.Next;<br> }<br> }<br> }<br> }<br> </code></td></tr></table> <br> <table width="100%" cellpadding="3" cellspacing="0" border="0"><tr><td class="lg"> <code>using System;<br> using Db4objects.Db4o.Activation;<br> using Db4objects.Db4o.TA;<br> namespace Db4objects.Db4o.Tutorial.F1.Chapter8<br> {<br> public class Car : IActivatable <br> {<br> private readonly String _model;<br> private SensorReadout _history;<br> <br> [Transient]<br> private IActivator _activator;<br> public Car(String model) <br> {<br> this._model=model;<br> this._history=null;<br> }<br> public String Model<br> {<br> get<br> {<br> Activate(ActivationPurpose.Read);<br> return _model;<br> }<br> }<br> <br> public SensorReadout History<br> {<br> get<br> {<br> Activate(ActivationPurpose.Read);<br> return _history;<br> }<br> }<br> <br> public void snapshot() <br> { <br> AppendToHistory(new TemperatureSensorReadout(DateTime.Now,this,"oil", PollOilTemperature()));<br> AppendToHistory(new TemperatureSensorReadout(DateTime.Now, this, "water", PollWaterTemperature()));<br> }<br> protected double PollOilTemperature() <br> {<br> return 0.1* CountHistoryElements();<br> }<br> protected double PollWaterTemperature() <br> {<br> return 0.2* CountHistoryElements();<br> }<br> public override String ToString() <br> {<br> Activate(ActivationPurpose.Read);<br> return string.Format("{0}/{1}", _model, CountHistoryElements());<br> }<br> <br> private int CountHistoryElements() <br> {<br> Activate(ActivationPurpose.Read);<br> return (_history==null ? 0 : _history.CountElements());<br> }<br> <br> private void AppendToHistory(SensorReadout readout) <br> {<br> Activate(ActivationPurpose.Write);<br> if(_history==null) <br> {<br> _history=readout;<br> }<br> else <br> {<br> _history.Append(readout);<br> }<br> }<br> public void Activate(ActivationPurpose purpose) <br> {<br> if(_activator != null) <br> {<br> _activator.Activate(purpose);<br> }<br> }<br> public void Bind(IActivator activator) <br> {<br> if (_activator == activator)<br> {<br> return;<br> }<br> if (activator != null && null != _activator)<br> {<br> throw new System.InvalidOperationException();<br> }<br> _activator = activator;<br> }<br> }<br> }<br> </code></td></tr></table> <br> <table width="100%" cellpadding="3" cellspacing="0" border="0"><tr><td class="lg"> <code>using System;<br> using Db4objects.Db4o.Activation;<br> using Db4objects.Db4o.TA;<br> namespace Db4objects.Db4o.Tutorial.F1.Chapter8<br> {<br> public class SensorReadout : IActivatable<br> {<br> private readonly DateTime _time;<br> private readonly Car _car;<br> private String _description;<br> private SensorReadout _next;<br> [Transient] <br> private IActivator _activator;<br> protected SensorReadout(DateTime time, Car car, String description)<br> {<br> this._time = time;<br> this._car = car;<br> this._description = description;<br> this._next = null;<br> }<br> public Car Car<br> {<br> get<br> {<br> Activate(ActivationPurpose.Read);<br> return _car;<br> }<br> }<br> public DateTime Time<br> {<br> get<br> {<br> Activate(ActivationPurpose.Read);<br> return _time;<br> }<br> }<br> public String Description<br> {<br> get<br> {<br> Activate(ActivationPurpose.Read);<br> return _description;<br> }<br> set<br> {<br> Activate(ActivationPurpose.Write);<br> _description = value;<br> }<br> }<br> public SensorReadout Next<br> {<br> get<br> {<br> Activate(ActivationPurpose.Read);<br> return _next;<br> }<br> }<br> public void Append(SensorReadout readout)<br> {<br> Activate(ActivationPurpose.Write);<br> if (_next == null)<br> {<br> _next = readout;<br> }<br> else<br> {<br> _next.Append(readout);<br> }<br> }<br> public int CountElements()<br> {<br> Activate(ActivationPurpose.Read);<br> return (_next == null ? 1 : _next.CountElements() + 1);<br> }<br> public override String ToString()<br> {<br> Activate(ActivationPurpose.Read);<br> return String.Format("{0} : {1} : {2}", _car, _time, _description);<br> }<br> public void Activate(ActivationPurpose purpose)<br> {<br> if (_activator != null)<br> {<br> _activator.Activate(purpose);<br> }<br> }<br> public void Bind(IActivator activator)<br> {<br> if (_activator == activator)<br> {<br> return;<br> }<br> if (activator != null && null != _activator)<br> {<br> throw new System.InvalidOperationException();<br> }<br> _activator = activator;<br> }<br> }<br> }<br> </code></td></tr></table> <br> <table width="100%" cellpadding="3" cellspacing="0" border="0"><tr><td class="lg"> <code>using System;<br> using Db4objects.Db4o.Activation;<br> namespace Db4objects.Db4o.Tutorial.F1.Chapter8<br> {<br> public class TemperatureSensorReadout : SensorReadout<br> {<br> private readonly double _temperature;<br> public TemperatureSensorReadout(DateTime time, Car car, string description, double temperature)<br> : base(time, car, description)<br> {<br> this._temperature = temperature;<br> }<br> public double Temperature<br> {<br> get<br> {<br> Activate(ActivationPurpose.Read);<br> return _temperature;<br> }<br> }<br> public override String ToString()<br> {<br> Activate(ActivationPurpose.Read);<br> return string.Format("{0} temp : {1}", base.ToString(), _temperature);<br> }<br> }<br> }<br> </code></td></tr></table> <br> <br> <br> <br> <br><br><br><p align="center"><a href="http://www.db4o.com/" target=_top><small>www.db4o.com</small></a></p><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br></td></tr></table></div></body></html>