Search code examples
javajpa-2.0openjpa

OpenJPA: Cascading delete throws PersistenceException


I have been away from the Java world for a while, and I am new to JPA. I have a data model where WindTurbines can have several PerformanceCurves, which in turn will contain several PerfCurvePoints. I have set cascade=CascadeType.ALL and orphanRemoval=true on the relationships.

When I remove a PerformanceCurve from a WindTurbine, I get a PersistenceException because there are PerfCurvePoint records in the database that reference the PerformanceCurve.

I expected that the PerfCurvePoint records would be automatically deleted along with the orphaned PerformanceCurve, since I specified CascadeType.ALL on that relationship.

I know that I can work around this by clearing the PerfCurvePoints collection on the PerformanceCurve object and calling EntityManager.flush() before I remove the PerformanceCurve from the WindTurbine, but I don't like having to reference the EntityManager in that layer of our application. Is there a better way to handle this?

@Entity
public class WindTurbine implements Serializable {

    @OneToMany(mappedBy="turbine", fetch=FetchType.EAGER, cascade=CascadeType.ALL, orphanRemoval=true)
    private ArrayList<PerformanceCurve> perfCurves = new ArrayList<>();

    ...

}


@Entity
public class PerformanceCurve implements Serializable {

    @ManyToOne(optional=false)
    @JoinColumn(name="TURBINE_ID", referncedColumnName="TURBINE_ID")
    private WindTurbine turbine;

    @OneToMany(mappedBy("perfCurve", fetch=FetchType.EAGER, cascade=CascadeType.ALL, orphanRemoval=true)
    private ArrayList<PerfCurvePoint> points = new ArrayList<>();

    ...

}

@Entity
public class PerfCurvePoint implements Serializable {

    @ManyToOne(optional=false)
    @JoinColumn(name="PERF_CURVE_ID", referncedColumnName="PERF_CURVE_ID")
    private PerformanceCurve perfCurve;

    ...

}

public class Foo {

    public void Bar(WindTurbine turbine) {
        // The following line causes a PersistenceException to be thrown
        //   on flush/commit if the performance curve contains any points.
        turbine.getPerformanceCurves().remove(1);
    }

    public void Workaround(EntityManager em, WindTurbine turbine) {
        // This works, but it requires a call to 
        //   EntityManager.flush() that I would like to avoid.
        PerformanceCurve curve = turbine.getPerformanceCurves().get(1);
        curve.getPoints().clear();
        em.flush();
        turbine.getPerformanceCurves().remove(curve);
    }
}

EDIT: I am actually getting a PersistenceException on all cascading deletes, whether they are a result of orphan removal or not.


Solution

  • It turns out that OpenJPA needs to be configured so it is aware of existing foreign key constraints in the database, or else it may try to delete the parent before it deletes the children. The solution was to add this line to persistence.xml:

    <property name="openjpa.jdbc.SchemaFactory" value="native(ForeignKeys=true)" />