When defining your objects to be persisted and the relationships between them, it is often required to define dependencies between these related objects. What should happen when persisting an object and it relates to another object? What should happen to a related object when an object is deleted? You can define what happens with JDO and with DataNucleus. Let's take an example
public class Owner { private DrivingLicense license; private Collection cars; ... } public class DrivingLicense { private String serialNumber; ... } public class Car { private String registrationNumber; private Owner owner; ... }
So we have an Owner of a collection of vintage Car's (1-N), and the Owner has a DrivingLicense (1-1). We want to define lifecycle dependencies to match the relationships that we have between these objects. Firstly lets look at the basic Meta-Data for the objects.
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE jdo SYSTEM "file:/javax/jdo/jdo.dtd"> <jdo> <package name="com.mydomain.samples.cars"> <class name="Owner"> <field name="license" persistence-modifier="persistent"/> <field name="cars"> <collection element-type="com.mydomain.samples.cars.Car" mapped-by="owner"/> </field> </class> <class name="DrivingLicense"> <field name="serialNumber"/> </class> <class name="Car"> <field name="registrationNumber"/> <field name="owner" persistence-modifier="persistent"/> </class> </package> </jdo>
JDO defines a concept called persistence-by-reachability. This means that when you persist an object and it has a related persistable object then this other object is also persisted. So using our example if we do
Owner bob = new Owner("Bob Smith"); DrivingLicense license = new DrivingLicense("011234BX4J"); bob.setLicense(license); pm.makePersistent(bob); // "bob" knows about "license"
This results in both the Owner and the DrivingLicense objects being made persistent since the Owner is passed to the PM operation and it has a field referring to the unpersisted DrivingLicense object. So "reachability" will persist the license.
With DataNucleus you can actually turn off persistence-by-reachability for particular fields, by specifying in the MetaData a DataNucleus extension tag, as follows
<class name="Owner"> <field name="license" persistence-modifier="persistent"> <extension vendor-name="datanucleus" key="cascade-persist" value="false"/> </field> ... </class>
So with this specification when we call makePersistent() with an object of type Owner then the field "license" will not be persisted at that time.
As mentioned above JDO defines a concept called persistence-by-reachability. This applies not just to persist but also to update of objects, so when you update an object and its updated field has a persistable object then that will be persisted. So using our example if we do
Owner bob = (Owner)pm.getObjectById(id); DrivingLicense license2 = new DrivingLicense("233424BX4J"); bob.setLicense(license2); // "bob" knows about "license2"
So when this field is updated the new DrivingLicense object will be made persistent since it is reachable from the persistent Owner object.
With DataNucleus you can actually turn off update-by-reachability for particular fields, by specifying in the MetaData a DataNucleus extension tag, as follows
<class name="Owner"> <field name="license" persistence-modifier="persistent"> <extension vendor-name="datanucleus" key="cascade-update" value="false"/> </field> ... </class>
So with this specification when we call makePersistent() to update an object of type Owner then the field "license" will not be updated at that time.
So we have an inverse 1-N relationship (no join table) between our Owner and his precious Car's, and a 1-1 relationship between the Owner and his DrivingLicense, because without his license he wouldn't be able to drive the cars :-0. What will happen to the license and the cars when the owner dies ? Well in this particular case we want to define that the when the owner is deleted, then his license will also be deleted (since it is for him only), but that his cars will continue to exist, because his daughter will inherit them. In JDO this is called Dependent Fields. To utilise this concept to achieve our end goal we change the Meta-Data to be
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE jdo SYSTEM "file:/javax/jdo/jdo.dtd"> <jdo> <package name="com.mydomain.samples.cars"> <class name="Owner"> <field name="license" persistence-modifier="persistent" dependent="true"/> <field name="cars"> <collection element-type="com.mydomain.samples.cars.Car" mapped-by="owner" dependent-element="false"/> </field> </class> <class name="DrivingLicense"> <field name="serialNumber"/> </class> <class name="Car"> <field name="registrationNumber"/> <field name="owner" persistence-modifier="persistent" dependent="false"/> </class> </package> </jdo>
So it was as simple as just adding dependent and dependent-element attributes to our related fields. Notice that we also added one to the other end of the Owner-Car relationship, so that when a Car comes to the end of its life, the Owner will not die with it. It may be the case that the owner dies driving the car and they both die at the same time, but their deaths are independent!!
Just as we made use of dependent-element for collection fields, we also can make use of dependent-key and dependent-value for map fields, and dependent-element for array fields.
Dependent Fields is utilised in the following situations
With JDO2 you can use "dependent-field" as shown above. As an alternative, when using RDBMS, you can use the datastore-defined foreign keys and let the datastore built-in "referential integrity" look after such deletions. DataNucleus provides a PMF property datanucleus.deletionPolicy allowing enabling of this mode of operation.
The default setting of datanucleus.deletionPolicy is "JDO2" which performs deletion of related objects as follows
The other setting of datanucleus.deletionPolicy is "DataNucleus" which performs deletion of related objects as follows
So, as you can see, with the second option you have the ability to utilise datastore "referential integrity" checking using your MetaData-specified <foreign-key> elements.