Object Clone

Platform implementations of #clone is not compatible with TP.

Both java and .NET object implementations provide #clone method for default objects, which is enabled by implementing Cloneable/ICloneable interface. This implementation is a shallow clone, i.e. only the top-level object fields are duplicated, all the referenced(children) objects are only copied as references to the same object in the parent clone. But how it affects Transparent Persistence?

If you remember Transparent Persistence Implementation you must know that a special Activator field is used to bind an object to the object container. Consequently, the default clone will copy this Activatable field to the object's duplicate, which will produce ambiguity as the object container won't know which object should be activated for write.

Let's look how it will affect db4o in practice. We will use a usual Car class and make it cloneable. Use the following code to store a car object and it's clone:

TPCloneExample.java: storeCar
01private static void storeCar() throws CloneNotSupportedException { 02 new File(DB4O_FILE_NAME).delete(); 03 ObjectContainer container = database(Db4o.newConfiguration()); 04 if (container != null) { 05 try { 06 // create a car 07 Car car = new Car("BMW", new Pilot("Rubens Barrichello")); 08 container.store(car); 09 // clone 10 Car car1 = (Car)car.clone(); 11 container.store(car1); 12 } finally { 13 closeDatabase(); 14 } 15 } 16 }
 

So it works for the first store, but what if we will clone an object retrieved from the database?

TPCloneExample.java: testClone
01private static void testClone() throws CloneNotSupportedException{ 02 storeCar(); 03 Configuration configuration = configureTP(); 04 05 ObjectContainer container = database(configuration); 06 if (container != null) { 07 try { 08 ObjectSet result = container.queryByExample(new Car(null, null)); 09 listResult(result); 10 Car car = null; 11 Car car1 = null; 12 if (result.size() > 0) 13 { 14 car = (Car)result.get(0); 15 System.out.println("Retrieved car: " + car); 16 car1 = (Car)car.clone(); 17 System.out.println("Storing cloned car: " + car1); 18 container.store(car1); 19 } 20 } finally { 21 closeDatabase(); 22 } 23 } 24 }
 

The code above throws an exception when the cloned object is being bound to the object container. Luckily we can easily fix it by overriding #clone method and setting activator to null:

Car.java: Clone
1public Object clone() throws CloneNotSupportedException { 2 Car test = (Car)super.clone(); 3 test._activator = null; 4 return test; 5 }