JPA : Cascading Fields

When defining your objects to be persisted and the relationships between them, it is often required to define dependencies between these related objects. When persisting an object should we also persist any related objects? What should happen to a related object when an object is deleted ? Can the related object exist in its own right beyond the lifecycle of the other object, or should it be deleted along with the other object ? This behaviour can be defined with JPA and with DataNucleus. Lets take an example

@Entity
public class Owner
{
    @OneToOne
    private DrivingLicense license;

    @OneToMany(mappedBy="owner")
    private Collection cars;
    
    ...
}

@Entity
public class DrivingLicense
{
    private String serialNumber;
    
    ...
}

@Entity
public class Car
{
    private String registrationNumber;

    @ManyToOne
    private Owner owner;
    
    ...
}

So we have an Owner of a collection of vintage Car's, and the Owner has a DrivingLicense. We want to define lifecycle dependencies to match the relationships that we have between these objects. So in our example what we are going to do is

  • When an object is persisted/updated its related objects are also persisted/updated.
  • When an Owner object is deleted, its DrivingLicense is deleted too (since it cant exist without the person!
  • When an Owner object is deleted, the Cars continue to exist (since someone will buy them)
  • When a Car object is deleted, the Owner continues to exist (unless he/she dies in the Car, but that will be handled by a different mechanism in our application).

So we update our class to reflect this

@Entity
public class Owner
{
    @OneToOne(cascade=CascadeType.ALL)
    private DrivingLicense license;

    @OneToMany(mappedBy="owner", cascade={CascadeType.PERSIST, CascadeType.MERGE})
    private Collection cars;
    
    ...
}

@Entity
public class DrivingLicense
{
    private String serialNumber;
    
    ...
}

@Entity
public class Car
{
    private String registrationNumber;

    @ManyToOne(cascade={CascadeType.PERSIST, CascadeType.MERGE})
    private Owner owner;
    
    ...
}

So we make use of the cascade attribute of the relation annotations. We could express this similarly in XML

<entity-mappings>
    <entity class="mydomain.Owner">
        <attributes>
            <one-to-many name="cars">
                <cascade>
                    <cascade-persist/>
                    <cascade-merge/>
                </cascade>
            </one-to-many>
            <one-to-one name="license">
                <cascade>
                    <cascade-all/>
                </cascade>
            </one-to-one>
            ...
        </attributes>
    </entity>

    <entity class="mydomain.DrivingLicense">
        ...
    </entity>

    <entity class="mydomain.Car">
        <attributes>
            <many-to-one name="owner">
                <cascade>
                    <cascade-persist/>
                    <cascade-merge/>
                </cascade>
            </many-to-one>
            ...
        </attributes>
    </entity>
</entity-mappings>

Orphans

When an element is removed from a collection, or when a 1-1 relation is nulled, sometimes it is desirable to delete the other object. JPA2 defines a facility of removing "orphans" by specifying metadata for a 1-1 or 1-N relation. Let's take an example. In the above relation between Owner and DrivingLicense if we set the owners license field to null, this should mean that the license is deleted. So we could change it to be

@Entity
public class Owner
{
    @OneToOne(cascade={CascadeType.PERSIST, CascadeType.MERGE}, orphanRemoval=true)
    private DrivingLicense license;

    ...
}

@Entity
public class DrivingLicense
{
    private String serialNumber;
    
    ...
}

So from now on, if we delete the Owner we delete the DrivingLicense, and if we set the license field of DrivingLicense to null then we also delete the DrivingLicense.