|
9. Transparent Activation
Let's take a second look at the concept of Activation. We have seen how db4o uses a "depth" concept by default to activate objects to a specific depth when they are returned from a query.
Wouldn't it be a lot nicer, if an application would never have to worry about activating objects and if db4o could handle things transparently for us? This is what Transparent Activation was developed for.
9.1. The Activation Problem
We can reuse most of the code from the Deep Graphs chapter and get it to work with Transparent Activation.
As a first step we should fill up our database with Car, Pilot and SensorReadout objects, so we have some objects to work with.
// storeCarAndSnapshots
Pilot pilot=new Pilot("Kimi Raikkonen",110);
Car car=new Car("Ferrari");
car.setPilot(pilot);
for(int i=0;i<5;i++) {
car.snapshot();
}
db.store(car); | |
If we now rerun the code to traverse all cars and their sensor readings, we are again confronted with the same problem that we had before, we end up with some leaves of our object graph being null.
// retrieveSnapshotsSequentially
ObjectSet result=db.queryByExample(Car.class);
Car car=(Car)result.next();
SensorReadout readout=car.getHistory();
while(readout!=null) {
System.out.println(readout);
readout=readout.getNext();
} | |
9.2. Turning On Transparent Activation
Let's configure db4o to run in Transparent Activation mode and let's try again:
// configureTransparentActivation
Db4o.configure().add(new TransparentActivationSupport()); | |
// retrieveSnapshotsSequentially
ObjectSet result=db.queryByExample(Car.class);
Car car=(Car)result.next();
SensorReadout readout=car.getHistory();
while(readout!=null) {
System.out.println(readout);
readout=readout.getNext();
} | |
Wow it worked! Is it really that easy? Principally yes. When db4o is run in Transparent Activation mode there are no surprises with null members that have not yet been read from the database.
9.3. Implementing Activatable
When Transparent Activation is turned on, all objects that do not implement the com.db4o.ta.Activatable interface will be fully activated when they are used.
Although we wont get any surprises with null objects when we work in this mode, access to the root of a deep graph may load the entire graph into memory and that can take a long time and use a lot of memory.
To prevent immediate activation of a class you can implement the 'Activatable' interface. Whenever db4o comes across an 'Activatable' object while activating a graph of objects it will stop traversing the graph any deeper. db4o will "know" that it can activate 'Activatable objects on demand, so there is no reason to continue activation until these objects are really needed.
For demonstration purposes we have made all classes used in this example 'Activatable' and we have also added all the code required to activate by hand.
Let's take a look at the 'Activatable' version of our Car class:
package com.db4o.f1.chapter7;
import java.util.*;
import com.db4o.activation.*;
import com.db4o.ta.*;
public class Car implements Activatable {
private String model;
private Pilot pilot;
private SensorReadout history;
private transient Activator _activator;
public Car(String model) {
this.model=model;
this.pilot=null;
this.history=null;
}
public Pilot getPilot() {
activate(ActivationPurpose.READ);
return pilot;
}
public Pilot getPilotWithoutActivation() {
return pilot;
}
public void setPilot(Pilot pilot) {
this.pilot=pilot;
}
public String getModel() {
activate(ActivationPurpose.READ);
return model;
}
public SensorReadout getHistory() {
activate(ActivationPurpose.READ);
return history;
}
public void snapshot() {
activate(ActivationPurpose.WRITE);
appendToHistory(new TemperatureSensorReadout(
new Date(),this,"oil",pollOilTemperature()));
appendToHistory(new TemperatureSensorReadout(
new Date(),this,"water",pollWaterTemperature()));
appendToHistory(new PressureSensorReadout(
new Date(),this,"oil",pollOilPressure()));
}
protected double pollOilTemperature() {
return 0.1*countHistoryElements();
}
protected double pollWaterTemperature() {
return 0.2*countHistoryElements();
}
protected double pollOilPressure() {
return 0.3*countHistoryElements();
}
public String toString() {
activate(ActivationPurpose.READ);
return model+"["+pilot+"]/"+countHistoryElements();
}
private int countHistoryElements() {
activate(ActivationPurpose.READ);
return (history==null ? 0 : history.countElements());
}
private void appendToHistory(SensorReadout readout) {
activate(ActivationPurpose.WRITE);
if(history==null) {
history=readout;
}
else {
history.append(readout);
}
}
public void activate(ActivationPurpose purpose) {
if(_activator != null) {
_activator.activate(purpose);
}
}
public void bind(Activator activator) {
if (_activator == activator) {
return;
}
if (activator != null && _activator != null) {
throw new IllegalStateException();
}
_activator = activator;
}
}
|
Can you spot the member _activator, all the # activate() calls and the two methods # activate() and # bind(Activator) at the end?
An Activatable class should store the Activator that db4o provides to the bind method in a transient variable and call # activate() on this Activator before any field is accessed.
If the object is already activated, the method will return immediately. If it is not, activation will happen at this time.
We have added the #getPilotWithoutActivation() method to the Car class to be able to demonstrate.
// demonstrateTransparentActivation
ObjectSet result=db.queryByExample(Car.class);
Car car=(Car)result.next();
System.out.println("#getPilotWithoutActivation() before the car is activated");
System.out.println(car.getPilotWithoutActivation());
System.out.println("calling #getPilot() activates the car object");
System.out.println(car.getPilot());
System.out.println("#getPilotWithoutActivation() after the car is activated");
System.out.println(car.getPilotWithoutActivation()); | |
9.4. Where Enhancement can help
If all this 'Activatable' code in a persistent class looked like a lot of typing work, do not worry: db4o comes with a tool to add this code automatically to all of your persistent classes. Read more about it in the chapter on Enhancement .
As a final step we should clean up the database again.
// deleteAll
ObjectSet result=db.queryByExample(new Object());
while(result.hasNext()) {
db.delete(result.next());
} | |
9.5. Conclusion
This was just a short introduction to Transparent Activation and what it can do for you. For more detailed information please see the pages on Transparent Activation in our online reference or in your offline copy of the Reference documentation.
9.6. Full source
package com.db4o.f1.chapter7;
import java.io.*;
import com.db4o.*;
import com.db4o.f1.*;
import com.db4o.ta.*;
public class TransparentActivationExample extends Util {
public static void main(String[] args) throws Exception{
// System.out.println(new File(Util.DB4OFILENAME).getCanonicalPath());
new File(Util.DB4OFILENAME).delete();
ObjectContainer db=Db4o.openFile(Util.DB4OFILENAME);
try {
storeCarAndSnapshots(db);
db.close();
db=Db4o.openFile(Util.DB4OFILENAME);
retrieveSnapshotsSequentially(db);
db.close();
configureTransparentActivation();
db=Db4o.openFile(Util.DB4OFILENAME);
retrieveSnapshotsSequentially(db);
db.close();
db=Db4o.openFile(Util.DB4OFILENAME);
demonstrateTransparentActivation(db);
db.close();
}
finally {
db.close();
}
}
public static void configureTransparentActivation(){
Db4o.configure().add(new TransparentActivationSupport());
}
public static void setCascadeOnUpdate() {
Db4o.configure().objectClass(Car.class).cascadeOnUpdate(true);
}
public static void storeCarAndSnapshots(ObjectContainer db) {
Pilot pilot=new Pilot("Kimi Raikkonen",110);
Car car=new Car("Ferrari");
car.setPilot(pilot);
for(int i=0;i<5;i++) {
car.snapshot();
}
db.store(car);
}
public static void retrieveSnapshotsSequentially(
ObjectContainer db) {
ObjectSet result=db.queryByExample(Car.class);
Car car=(Car)result.next();
SensorReadout readout=car.getHistory();
while(readout!=null) {
System.out.println(readout);
readout=readout.getNext();
}
}
public static void demonstrateTransparentActivation(
ObjectContainer db) {
ObjectSet result=db.queryByExample(Car.class);
Car car=(Car)result.next();
System.out.println("#getPilotWithoutActivation() before the car is activated");
System.out.println(car.getPilotWithoutActivation());
System.out.println("calling #getPilot() activates the car object");
System.out.println(car.getPilot());
System.out.println("#getPilotWithoutActivation() after the car is activated");
System.out.println(car.getPilotWithoutActivation());
}
}
|
www.db4o.com
|