Issue Details (XML | Word | Printable)

Key: OPENJPA-181
Type: Bug Bug
Status: Resolved Resolved
Resolution: Fixed
Priority: Major Major
Assignee: Unassigned
Reporter: Jonathan Feinberg
Votes: 0
Watchers: 0
Operations

If you were logged in you would be able to see more operations.
OpenJPA

ClassCastException when executing bulk delete on an entity that owns a OneToOne with a Cascade.DELETE when DataCache is on

Created: 26/Mar/07 03:26 PM   Updated: 27/Mar/07 07:25 PM
Return to search
Component/s: kernel
Affects Version/s: 0.9.7
Fix Version/s: 0.9.7

Time Tracking:
Not Specified

Resolution Date: 27/Mar/07 07:25 PM


 Description  « Hide
Given an entity class A which owns a OneToOne entity of class B, and given a cascade on that OneToOne that includes DELETE, an attempt to bulk-delete A when using the DataCache results in a stack trace like the following:

java.lang.ClassCastException: org.apache.openjpa.datacache.QueryCacheStoreQuery cannot be cast to org.apache.openjpa.kernel.ExpressionStoreQuery
    at org.apache.openjpa.kernel.ExpressionStoreQuery$DataStoreExecutor.executeQuery(ExpressionStoreQuery.java:674)
    at org.apache.openjpa.kernel.QueryImpl.execute(QueryImpl.java:979)
    at org.apache.openjpa.kernel.QueryImpl.deleteInMemory(QueryImpl.java:1005)
    ... 28 more

The proximate cause for the bug is that when the JDBCStoreQuery does this:

    private Table getTable(FieldMapping fm, Table table) {
        if (fm.getCascadeDelete() != ValueMetaData.CASCADE_NONE)
            return INVALID;

it causes "isSingleTableMapping" to be considered false, which in turn permits executeBulkOperation to return null. Meanwhile, back in DataStoreExecutor:

       public Number executeDelete(StoreQuery q, Object[] params) {
            Number num = ((ExpressionStoreQuery) q).executeDelete(this, _meta,
                _metas, _subs, _facts, _exps, params);
            if (num == null)
                return q.getContext().deleteInMemory(this, params); // <- now we have come here because executeDelete punted
            return num;
        }

So deleteInMemory gets called in QueryImpl:

   public Number deleteInMemory(StoreQuery.Executor executor,
        Object[] params) {
        try {
            Object o = execute(executor, params);

, but a DataStoreExecutor doesn't know how to execute the QueryCacheStoreQuery that it gets.

Somehwere, something is too unwrapped, or not wrapped enough. Good luck!

Workaround:

If A owns B, then instead of cascade=CascadeType.ALL, you can

@Entity
class A {
    B myThing;

    @OneToOne(cascade = { CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH })
   B getMyThing() { return myThing; }
}

@Entity
class B {
    A owner;

    @ForeignKey(deleteAction=ForeignKeyAction.CASCADE)
    A getOwner() { return owner; }
}

 All   Comments   Work Log   Change History   Subversion Commits      Sort Order: Ascending order - Click to sort in descending order
Abe White added a comment - 27/Mar/07 07:25 PM
Fixed in SVN revision 523046. See test case added to TestBulkJPQLAndDataCache.