DescendIntoTranslator

Translator API provides a special way of storing and retrieving objects. In fact the actual class is not stored in the database. Instead the information from that class is stored in a primitive object (object array) and the class is recreated during instantiation or activation.

Let's look how queries handle translated classes. Diagnostics system will help us to see, what is going on.

In our example class Car is configured to be saved and retrieved with CarTranslator class. CarTranslator saves only car model information appending it with the production date.

CarTranslator.java
01package com.db4odoc.f1.diagnostics; 02 03import com.db4o.*; 04import com.db4o.config.*; 05 06public class CarTranslator 07 implements ObjectConstructor { 08 public Object onStore(ObjectContainer container, 09 Object applicationObject) { 10 Car car =(Car)applicationObject; 11 12 String fullModel; 13 if (hasYear(car.getModel())){ 14 fullModel = car.getModel(); 15 } else { 16 fullModel = car.getModel() + getYear(car.getModel()); 17 } 18 return new Object[]{fullModel}; 19 } 20 21 private String getYear(String carModel){ 22 if (carModel.equals("BMW")){ 23 return " 2002"; 24 } else { 25 return " 1999"; 26 } 27 28 } 29 30 private boolean hasYear(String carModel){ 31 return false; 32 } 33 34 public Object onInstantiate(ObjectContainer container, Object storedObject) { 35 Object[] raw=(Object[])storedObject; 36 String model=(String)raw[0]; 37 return new Car(model); 38 } 39 40 public void onActivate(ObjectContainer container, 41 Object applicationObject, Object storedObject) { 42 } 43 44 public Class storedClass() { 45 return Object[].class; 46 } 47}

Let's clean our database and store 2 cars:

DiagnosticExample.java: storeTranslatedCars
01public static void storeTranslatedCars() { 02 Db4o.configure().exceptionsOnNotStorable(true); 03 Db4o.configure().objectClass(Car.class).translate(new CarTranslator()); 04 Db4o.configure().objectClass(Car.class).callConstructor(true); 05 new File(YAPFILENAME).delete(); 06 ObjectContainer db = Db4o.openFile(YAPFILENAME); 07 try { 08 Car car1 = new Car("BMW"); 09 System.out.println("ORIGINAL: " + car1); 10 db.set(car1); 11 Car car2 = new Car("Ferrari"); 12 System.out.println("ORIGINAL: " + car2); 13 db.set(car2); 14 } catch (Exception exc) { 15 System.out.println(exc.toString()); 16 return; 17 } finally { 18 db.close(); 19 } 20 }

We can check the contents of our database with the following method:

DiagnosticExample.java: retrieveTranslatedCars
01public static void retrieveTranslatedCars() { 02 Db4o.configure().diagnostic().addListener(new TranslatorDiagListener()); 03 Db4o.configure().exceptionsOnNotStorable(true); 04 Db4o.configure().objectClass(Car.class).translate(new CarTranslator()); 05 Db4o.configure().objectClass(Car.class).callConstructor(true); 06 ObjectContainer db = Db4o.openFile(YAPFILENAME); 07 try { 08 ObjectSet result = db.query(Car.class); 09 listResult(result); 10 } finally { 11 db.close(); 12 Db4o.configure().diagnostic().removeAllListeners(); 13 } 14 }

TranslatorDiagListener is implemented to help us filter only those diagnostic messages, that concern translated classes (filtering diagnostics messages is explained in Diagnostic Messages Filter chapter).

We did not get any diagnostic messages here and the result shows the stored cars with extended model values.

To test Native Queries we will use the predicate, which retrieves only cars, produced in year 2002:

NewCarModel.java
1package com.db4odoc.f1.diagnostics; 2 3import com.db4o.query.Predicate; 4 5public class NewCarModel extends Predicate<Car> { 6 public boolean match(Car car) { 7 return ((Car)car).getModel().endsWith("2002"); 8 } 9}

DiagnosticExample.java: retrieveTranslatedCarsNQ
01public static void retrieveTranslatedCarsNQ() { 02 Db4o.configure().diagnostic().addListener(new TranslatorDiagListener()); 03 Db4o.configure().exceptionsOnNotStorable(true); 04 Db4o.configure().objectClass(Car.class).translate(new CarTranslator()); 05 Db4o.configure().objectClass(Car.class).callConstructor(true); 06 ObjectContainer db = Db4o.openFile(YAPFILENAME); 07 try { 08 ObjectSet result = db.query(new NewCarModel()); 09 listResult(result); 10 } finally { 11 db.close(); 12 Db4o.configure().diagnostic().removeAllListeners(); 13 } 14 }

A diagnostic message should appear pointing out, that the query is not correct in our case. Let's try  to correct it using unoptimized NQ and evaluations.

DiagnosticExample.java: retrieveTranslatedCarsNQUnopt
01public static void retrieveTranslatedCarsNQUnopt() { 02 Db4o.configure().optimizeNativeQueries(false); 03 Db4o.configure().diagnostic().addListener(new TranslatorDiagListener()); 04 Db4o.configure().exceptionsOnNotStorable(true); 05 Db4o.configure().objectClass(Car.class).translate(new CarTranslator()); 06 Db4o.configure().objectClass(Car.class).callConstructor(true); 07 ObjectContainer db = Db4o.openFile(YAPFILENAME); 08 try { 09 ObjectSet result = db.query(new NewCarModel()); 10 listResult(result); 11 } finally { 12 Db4o.configure().optimizeNativeQueries(true); 13 db.close(); 14 Db4o.configure().diagnostic().removeAllListeners(); 15 } 16 }

We will use simple evaluation to check our cars:

CarEvaluation.java
01package com.db4odoc.f1.diagnostics; 02 03import com.db4o.query.*; 04 05public class CarEvaluation implements Evaluation { 06 public void evaluate(Candidate candidate) 07 { 08 Car car=(Car)candidate.getObject(); 09 candidate.include(car.getModel().endsWith("2002")); 10 } 11}

DiagnosticExample.java: retrieveTranslatedCarsSODAEv
01public static void retrieveTranslatedCarsSODAEv() { 02 Db4o.configure().diagnostic().addListener(new TranslatorDiagListener()); 03 Db4o.configure().exceptionsOnNotStorable(true); 04 Db4o.configure().objectClass(Car.class).translate(new CarTranslator()); 05 Db4o.configure().objectClass(Car.class).callConstructor(true); 06 ObjectContainer db = Db4o.openFile(YAPFILENAME); 07 try { 08 Query query = db.query(); 09 query.constrain(Car.class); 10 query.constrain(new CarEvaluation()); 11 ObjectSet result = query.execute(); 12 listResult(result); 13 } finally { 14 db.close(); 15 Db4o.configure().diagnostic().removeAllListeners(); 16 Db4o.configure().objectClass(Car.class).translate(null); 17 } 18 }

In both cases we the results are correct. Native Query optimization cannot be used with the translated classes, because the actual values of the translated fields are only known after instantiation and activation. That also means that translated classes can have a considerable impact on database performance and should be used with care.