You can inject transparent persistence awareness in your persisted classes without modifying their original code. This is done by enhancing the class-files at build time.
For transparent activation/persistence you need following dependencies at compile time. (see also the dependency overview)
The first step is to enhance the persisted classes. One possibility is to introduce an Annotation to mark your persisted classes.
By the way, their are alternative way to select the enhanced classes. See here.
@Retention(RetentionPolicy.RUNTIME) @Documented @Target(ElementType.TYPE) public @interface TransparentPersisted { }
This Annotationis then used to mark all persisted classes.
@TransparentPersisted public class Person {
The next step is to create a class filter which reports all classes which should be enhanced. There filter checks for the presence of the annotation.
public final class AnnotationFilter implements ClassFilter { public boolean accept(Class<?> aClass) { if(null==aClass || aClass.equals(Object.class)){ return false; } return hasAnnotation(aClass) || accept(aClass.getSuperclass()); } private boolean hasAnnotation(Class<?> aClass) { // We compare by name, to be class-loader independent Annotation[] annotations = aClass.getAnnotations(); for (Annotation annotation : annotations) { if(annotation.annotationType().getName() .equals(TransparentPersisted.class.getName())){ return true; } } return false; } }
This enhancement step injects the required bytecode into the domain classes to support transparent activation/persistence.
<target name="enhance"> <!-- Change these according to your project --> <property name="target" value="./target/classes/"/> <property name="libraries" value="./lib/"/> <path id="project.classpath"> <pathelement path="${target}"/> <fileset dir="${libraries}"> <include name="*.jar"/> </fileset> </path> <!-- We enhance with an additional Ant-run step. You can put this also in an extra file --> <typedef resource="instrumentation-def.properties" classpathref="project.classpath" loaderRef="instrumentation.loader"/> <!-- We filter by our annotation --> <typedef name="annotation-filter" classname="com.db4odoc.tp.enhancement.AnnotationFilter" classpathref="project.classpath" loaderRef="instrumentation.loader"/> <db4o-instrument classTargetDir="${target}" verbose="true"> <classpath refid="project.classpath"/> <sources dir="${target}"> <include name="**/*.class"/> </sources> <transparent-activation-step> <annotation-filter/> </transparent-activation-step> </db4o-instrument> </target>
You can configure Eclipse to run the Ant build with each compile step. Right click on your project and choose 'Properties'. Then switch to 'Builders' and add a new one. Choose the 'Ant Builder'. On the new window choose the build-file which contains the example-code. Switch to the 'Targets'-Tab. There choose the enhance-target for the 'Auto-Build'. Now the enhancer-task will be run by Eclipse automatically. The example project above is configured this way.
It's also possible to enhance with Maven by using the Ant plugin.
<plugin> <artifactId>maven-antrun-plugin</artifactId> <version>1.6</version> <dependencies> <!-- We need the db4o tooling for enhancing stuff --> <dependency> <groupId>com.db4o</groupId> <artifactId>db4o-tools-java5</artifactId> <version>8.0-SNAPSHOT</version> </dependency> </dependencies> <executions> <execution> <phase>compile</phase> <configuration> <target> <!-- We enhance with an additional Ant-run step. You can put this also in an extra file --> <typedef resource="instrumentation-def.properties" classpathref="maven.compile.classpath"/> <!-- We filter by our annotation --> <typedef name="annotation-filter" classname="com.db4odoc.tp.enhancement.AnnotationFilter" classpathref="maven.compile.classpath"/> <db4o-instrument classTargetDir="target/classes"> <classpath refid="maven.compile.classpath"/> <sources dir="target/classes"> <include name="**/*.class"/> </sources> <transparent-activation-step> <annotation-filter/> </transparent-activation-step> </db4o-instrument> </target> </configuration> <goals> <goal>run</goal> </goals> </execution> </executions> </plugin>
You can check if the enhancement worked correctly by checking for the activation interface. Such a check should be part of your test-suite to ensure that everything works correctly.
if (!Activatable.class.isAssignableFrom(Person.class)) { throw new AssertionError("Expect that the " + Person.class + " implements " + Activatable.class); }
Configure the transparent activation in order to use it.
final EmbeddedConfiguration configuration = Db4oEmbedded.newConfiguration(); configuration.common().add(new TransparentActivationSupport()); ObjectContainer container = Db4oEmbedded.openFile(configuration, DATABASE_FILE_NAME);
After that transparent activation is working properly you can transverse along the object graph and don’t have to worry about not activated objects:
Person person = queryByName(container, "Joanna the 10"); Person beginOfDynasty = person.getMother(); // With transparent activation enabled, you can navigate deeply // nested object graphs. db4o will ensure that the objects // are loaded from the database. while (null != beginOfDynasty.getMother()) { beginOfDynasty = beginOfDynasty.getMother(); } System.out.println(beginOfDynasty.getName());
Transparent persistence not only manages the activation, but also manages updating the objects. Configure transparent persistence in order to use it:
final EmbeddedConfiguration configuration = Db4oEmbedded.newConfiguration(); configuration.common().add(new TransparentPersistenceSupport(new DeactivatingRollbackStrategy())); ObjectContainer container = Db4oEmbedded.openFile(configuration, DATABASE_FILE_NAME);
After that updated objects are automatically stored every time you commit.
Person person = queryByName(container, "Joanna the 10"); Person mother = person.getMother(); mother.setName("New Name"); // Just commit the transaction. All modified objects are stored container.commit();