OK, so I have given Lifecycle a try, but now I am re-stuck.
So, modifying my example above, I got rid of the LifecycleInterceptor and Deletable classes altogether. Now I have:
Code:
public class WorkRequest extends com.nimblefish.core.domain.generated.WorkRequestGen
implements Lifecycle {
...
public boolean onDelete(Session session) throws CallbackException {
try {
WorkFinder.deleteWorkRequestDependentJobs(this.getId());
} catch (NfPersistenceException e) {
throw new CallbackException("Couldn't delete dependent jobs", e);
}
return false;
}
...
}
And everything else as above, with the sole exception of another dependent mapping from Job to Task (which I didn't show before but now is relevant):
Code:
<class name="com.nimblefish.core.domain.work.Job">
<id name="id" type="long" unsaved-value="null" >
<meta attribute="scope-set">private</meta>
<generator class="native"/>
</id>
<!-- to enable polymorphism -->
<discriminator/>
<property name="status" type="com.nimblefish.core.domain.work.Status"/>
<property name="percentComplete" type="float"/>
<many-to-one name="workRequest" column="workrequest_id"
class="com.nimblefish.core.domain.work.WorkRequest"/>
<!-- what are the tasks for this job? -->
<set name="tasks" cascade="all-delete-orphan" lazy="true" inverse="true">
<key column="job_id"/>
<one-to-many class="com.nimblefish.core.domain.work.Task"/>
</set>
...
<subclass name="com.nimblefish.core.domain.work.ImportJob"
extends="com.nimblefish.core.domain.work.Job"
discriminator-value="IMP">
<meta attribute="generated-class">com.nimblefish.core.domain.generated.ImportJobGen</meta>
</class>
<class name="com.nimblefish.core.domain.work.Task">
<id name="id" type="long" unsaved-value="null" >
<meta attribute="scope-set">private</meta>
<generator class="native"/>
</id>
<discriminator/>
<many-to-one name="job" column="job_id" not-null="true"
class="com.nimblefish.core.domain.work.Job"/>
<subclass name="com.nimblefish.core.domain.work.ImportTask"
extends="com.nimblefish.core.domain.work.Task"
discriminator-value="IMP">
<meta attribute="generated-class">com.nimblefish.core.domain.generated.ImportTaskGen</meta>
</subclass>
</class>
I am testing this with test code that used to work just fine in a previous version, when I just had a straight parent-to-child relationship from WorkRequest to Job, and then from Job to Task. In that version, I could cascade a delete to WorkRequest, and it would cascade all the way down to Task with no problems. (So I'm not totally ignorant of parent-child issues ;-)
The change I am making, using Lifecycle and creating a non-one-to-many mapping from WorkRequest to Job, seems to be breaking this. In the new version, with the Lifecycle-aware WorkRequest, this cascading delete fails with a foreign key constraint violation. Here's the Hibernate debug output, with as much detail elided as I could:
Code:
[junit] 17:25:56,291 DEBUG PersistenceManagerTest:245 - *** Removing work request...
[junit] 17:25:56,307 DEBUG SessionImpl:2193 - flushing session
[junit] 17:25:56,307 DEBUG Cascades:497 - processing cascades for: com.nimblefish.core.domain.campaign.Client
[junit] 17:25:56,307 DEBUG Cascades:524 - cascading to collection: com.nimblefish.core.domain.campaign.Client.campaigns
[junit] 17:25:56,323 DEBUG Cascades:113 - cascading to saveOrUpdate()
[junit] 17:25:56,323 DEBUG SessionImpl:1306 - saveOrUpdate() persistent instance
[junit] 17:25:56,323 DEBUG Cascades:506 - done processing cascades for: com.nimblefish.core.domain.campaign.Client
[junit] 17:25:56,338 DEBUG Cascades:497 - processing cascades for: com.nimblefish.core.domain.campaign.Campaign
[junit] 17:25:56,338 DEBUG Cascades:524 - cascading to collection: com.nimblefish.core.domain.campaign.Campaign.assets
[junit] 17:25:56,354 DEBUG Cascades:524 - cascading to collection: com.nimblefish.core.domain.campaign.Campaign.workRequests
[junit] 17:25:56,354 DEBUG SessionImpl:1099 - deleting a persistent instance
[junit] 17:25:56,370 DEBUG SessionImpl:1119 - deleting [com.nimblefish.core.domain.work.WorkRequest#1]
[junit] 17:25:56,370 DEBUG SessionImpl:1146 - calling onDelete()
[junit] 17:25:56,385 DEBUG WorkFinder:107 - Deleting jobs dependent on work request 1
[junit] 17:25:56,401 DEBUG SessionImpl:1460 - find: from Job job where job.workRequest.id = ?
.....
[junit] 17:25:57,213 DEBUG WorkFinder:125 - Deleting job com.nimblefish.core.domain.work.ImportJob@15cd9c0[id=1]
[junit] 17:25:57,213 DEBUG SessionImpl:1099 - deleting a persistent instance
[junit] 17:25:57,229 DEBUG SessionImpl:1119 - deleting [com.nimblefish.core.domain.work.ImportJob#1]
[junit] 17:25:57,229 DEBUG Cascades:497 - processing cascades for: com.nimblefish.core.domain.work.ImportJob
[junit] 17:25:57,245 DEBUG Cascades:524 - cascading to collection: com.nimblefish.core.domain.work.Job.tasks
[junit] 17:25:57,245 DEBUG SessionImpl:3132 - initializing collection [com.nimblefish.core.domain.work.Job.tasks#1]
.....
[junit] 17:25:57,745 DEBUG SessionImpl:2678 - Scheduling collection removes/(re)creates/updates
[junit] 17:25:57,745 DEBUG SessionImpl:2217 - Flushed: 0 insertions, 0 updates, 3 deletions to 5 objects
[junit] 17:25:57,745 DEBUG SessionImpl:2222 - Flushed: 0 (re)creations, 0 updates, 7 removals to 12 collections
[junit] 17:25:57,760 DEBUG Printer:75 - listing entities:
[junit] 17:25:57,760 DEBUG Printer:82 - com.nimblefish.core.domain.work.ImportTask{endRecord=100, job=ImportJob#1, inputCsvVersion=null, startRecord=0, status=0, id=1}
[junit] 17:25:57,776 DEBUG Printer:82 - com.nimblefish.core.domain.work.ImportJob{outputVersions=uninitialized, outputLists=uninitialized, predecessorJobs=[], WorkRequest=WorkRequest#1, inputVersions=uninitialized, successorJobs=[], percentComplete=0.0, status=0, inputLists=uninitialized, tasks=[ImportTask#1], id=1}
[junit] 17:25:57,791 DEBUG Printer:82 - com.nimblefish.core.domain.work.WorkRequest{lastTaskTime=0, campaign=Campaign#1, status=0, initialJobs=uninitialized, name=test work request, id=1}
[junit] 17:25:57,791 DEBUG Printer:82 - com.nimblefish.core.domain.campaign.Campaign{client=Client#1, assets=uninitialized, workRequests=[], name=test1, id=1, lists=uninitialized}
[junit] 17:25:57,807 DEBUG Printer:82 - com.nimblefish.core.domain.campaign.Client{campaigns=[Campaign#1], name=test, id=1}
[junit] 17:25:57,807 DEBUG SessionImpl:2258 - executing flush
[junit] 17:25:57,823 DEBUG BasicCollectionPersister:491 - Deleting collection: [com.nimblefish.core.domain.work.Job.inputLists#1]
[junit] 17:25:57,838 DEBUG BatcherImpl:192 - about to open: 0 open PreparedStatements, 0 open ResultSets
[junit] 17:25:57,838 DEBUG SQL:223 - delete from job_to_input_list where list_id=?
[junit] 17:25:57,854 DEBUG BatcherImpl:227 - preparing statement
[junit] 17:25:57,854 DEBUG LongType:46 - binding '1' to parameter: 1
[junit] 17:25:57,854 DEBUG BatcherImpl:28 - Adding to batch
[junit] 17:25:57,854 DEBUG BasicCollectionPersister:507 - done deleting collection
[junit] 17:25:57,870 DEBUG BasicCollectionPersister:491 - Deleting collection: [com.nimblefish.core.domain.work.Job.inputVersions#1]
[junit] 17:25:57,870 DEBUG BatcherImpl:50 - Executing batch size: 1
[junit] 17:25:57,870 DEBUG BatcherImpl:199 - done closing: 0 open PreparedStatements, 0 open ResultSets
[junit] 17:25:57,885 DEBUG BatcherImpl:240 - closing statement
[junit] 17:25:57,885 DEBUG BatcherImpl:192 - about to open: 0 open PreparedStatements, 0 open ResultSets
[junit] 17:25:57,901 DEBUG SQL:223 - delete from job_to_input_version where list_id=?
[junit] 17:25:57,901 DEBUG BatcherImpl:227 - preparing statement
[junit] 17:25:57,916 DEBUG LongType:46 - binding '1' to parameter: 1
[junit] 17:25:57,916 DEBUG BatcherImpl:28 - Adding to batch
[junit] 17:25:57,932 DEBUG BasicCollectionPersister:507 - done deleting collection
[junit] 17:25:57,932 DEBUG BasicCollectionPersister:491 - Deleting collection: [com.nimblefish.core.domain.work.Job.outputLists#1]
[junit] 17:25:57,932 DEBUG BatcherImpl:50 - Executing batch size: 1
[junit] 17:25:57,948 DEBUG BatcherImpl:199 - done closing: 0 open PreparedStatements, 0 open ResultSets
[junit] 17:25:57,948 DEBUG BatcherImpl:240 - closing statement
[junit] 17:25:57,948 DEBUG BatcherImpl:192 - about to open: 0 open PreparedStatements, 0 open ResultSets
[junit] 17:25:57,963 DEBUG SQL:223 - delete from job_to_output_list where list_id=?
[junit] 17:25:57,963 DEBUG BatcherImpl:227 - preparing statement
[junit] 17:25:57,963 DEBUG LongType:46 - binding '1' to parameter: 1
[junit] 17:25:57,979 DEBUG BatcherImpl:28 - Adding to batch
[junit] 17:25:57,979 DEBUG BasicCollectionPersister:507 - done deleting collection
[junit] 17:25:57,979 DEBUG BasicCollectionPersister:491 - Deleting collection: [com.nimblefish.core.domain.work.Job.outputVersions#1]
[junit] 17:25:57,995 DEBUG BatcherImpl:50 - Executing batch size: 1
[junit] 17:25:58,010 DEBUG BatcherImpl:199 - done closing: 0 open PreparedStatements, 0 open ResultSets
[junit] 17:25:58,010 DEBUG BatcherImpl:240 - closing statement
[junit] 17:25:58,026 DEBUG BatcherImpl:192 - about to open: 0 open PreparedStatements, 0 open ResultSets
[junit] 17:25:58,026 DEBUG SQL:223 - delete from job_to_output_version where list_id=?
[junit] 17:25:58,041 DEBUG BatcherImpl:227 - preparing statement
[junit] 17:25:58,041 DEBUG LongType:46 - binding '1' to parameter: 1
[junit] 17:25:58,041 DEBUG BatcherImpl:28 - Adding to batch
[junit] 17:25:58,041 DEBUG BasicCollectionPersister:507 - done deleting collection
[junit] 17:25:58,057 DEBUG BatcherImpl:50 - Executing batch size: 1
[junit] 17:25:58,057 DEBUG BatcherImpl:199 - done closing: 0 open PreparedStatements, 0 open ResultSets
[junit] 17:25:58,057 DEBUG BatcherImpl:240 - closing statement
[junit] 17:25:58,073 DEBUG EntityPersister:553 - Deleting entity: [com.nimblefish.core.domain.work.WorkRequest#1]
[junit] 17:25:58,073 DEBUG BatcherImpl:192 - about to open: 0 open PreparedStatements, 0 open ResultSets
[junit] 17:25:58,088 DEBUG SQL:223 - delete from WorkRequest where id=?
[junit] 17:25:58,088 DEBUG BatcherImpl:227 - preparing statement
[junit] 17:25:58,088 DEBUG LongType:46 - binding '1' to parameter: 1
[junit] 17:25:58,104 DEBUG BatcherImpl:28 - Adding to batch
[junit] 17:25:58,104 DEBUG EntityPersister:553 - Deleting entity: [com.nimblefish.core.domain.work.ImportTask#1]
[junit] 17:25:58,120 DEBUG BatcherImpl:50 - Executing batch size: 1
[junit] 17:25:58,120 DEBUG JDBCExceptionReporter:36 - SQL Exception
[junit] java.sql.BatchUpdateException: General error, message from server: "Cannot delete or update a parent row: a foreign key constraint fails" [junit] at com.mysql.jdbc.PreparedStatement.executeBatch(PreparedStatement.java:1469)
[junit] at net.sf.hibernate.impl.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:54)
[junit] at net.sf.hibernate.impl.BatcherImpl.executeBatch(BatcherImpl.java:118)
[junit] at net.sf.hibernate.impl.BatcherImpl.prepareStatement(BatcherImpl.java:55)
[junit] at net.sf.hibernate.impl.BatcherImpl.prepareBatchStatement(BatcherImpl.java:105)
[junit] at net.sf.hibernate.persister.EntityPersister.delete(EntityPersister.java:565)
[junit] at net.sf.hibernate.impl.ScheduledDeletion.execute(ScheduledDeletion.java:29)
[junit] at net.sf.hibernate.impl.SessionImpl.executeAll(SessionImpl.java:2308)
[junit] at net.sf.hibernate.impl.SessionImpl.execute(SessionImpl.java:2266)
[junit] at net.sf.hibernate.impl.SessionImpl.flush(SessionImpl.java:2187)
[junit] at com.nimblefish.core.persist.Persistence.flush(Persistence.java:300)
[junit] at com.nimblefish.core.test.persist.PersistenceManagerTest.testTask(PersistenceManag
erTest.java:247)
So the upshot is that the cascade seems to work, insofar as the final list of entities to be deleted in the flush is correct. The final list of entities is a Client, a Campaign, a WorkRequest, an ImportJob, and an ImportTask, which is exactly right. And the foreign key violation seems to be being thrown when the batcher goes to delete the ImportTask. But there are no parent key constraints on the ImportTask! That is, I can't see which foreign key constraint is being violated here, given that it seems to be the ImportTask that is failing to be peacefully deleted.
Does anyone have any pointers as to how to get more information about which foreign key constraints are failing at the SQL / database level in this kind of cascade failure situation?
Thanks very, very much.
Cheers,
Rob