* relation information.
* @param sm StateManager for the owner
*/
public void preDelete(ObjectProvider sm)
{
ExecutionContext ec = sm.getExecutionContext();
// makes sure field is loaded
int fieldNumber = mmd.getAbsoluteFieldNumber();
try
{
sm.isLoaded(fieldNumber);
}
catch (JDOObjectNotFoundException onfe) // TODO Use different method that throws NucleusObjectNotFoundException
{
// Already deleted so just return
return;
}
Object pc = sm.provideField(fieldNumber);
if (pc == null)
{
// Null value so nothing to do
return;
}
// N-1 Uni, so delete join table entry
ClassLoaderResolver clr = sm.getExecutionContext().getClassLoaderResolver();
RelationType relationType = mmd.getRelationType(clr);
if (relationType == RelationType.MANY_TO_ONE_UNI)
{
// Update join table entry
PersistableRelationStore store =
(PersistableRelationStore) storeMgr.getBackingStoreForField(
sm.getExecutionContext().getClassLoaderResolver(), mmd, mmd.getType());
store.remove(sm);
}
// Check if we should delete the related object when this object is deleted
boolean dependent = mmd.isDependent();
if (mmd.isCascadeRemoveOrphans())
{
// JPA allows "orphan removal" to define deletion of the other side
dependent = true;
}
// Check if the field has a FK defined
AbstractMemberMetaData[] relatedMmds = mmd.getRelatedMemberMetaData(clr);
// TODO Cater for more than 1 related field
boolean hasFK = false;
if (!dependent)
{
// Not dependent, so check if the datastore has a FK and will take care of it for us
if (mmd.getForeignKeyMetaData() != null)
{
hasFK = true;
}
if (relatedMmds != null && relatedMmds[0].getForeignKeyMetaData() != null)
{
hasFK = true;
}
if (ec.getNucleusContext().getPersistenceConfiguration().getStringProperty(PropertyNames.PROPERTY_DELETION_POLICY).equals("JDO2"))
{
// JDO2 doesnt currently (2.0 spec) take note of foreign-key
hasFK = false;
}
}
// Basic rules for the following :-
// 1. If it is dependent then we delete it (maybe after nulling).
// 2. If it is not dependent and they have defined no FK then null it, else delete it
// 3. If it is not dependent and they have a FK, let the datastore handle the delete
// There may be some corner cases that this code doesn't yet cater for
if (relationType == RelationType.ONE_TO_ONE_UNI ||
(relationType == RelationType.ONE_TO_ONE_BI && mmd.getMappedBy() == null))
{
// 1-1 with FK at this side (owner of the relation)
if (dependent)
{
boolean relatedObjectDeleted = ec.getApiAdapter().isDeleted(pc);
if (isNullable() && !relatedObjectDeleted)
{
// Other object not yet deleted - just null out the FK
// TODO Not doing this would cause errors in 1-1 uni relations (e.g AttachDetachTest)
// TODO Log this since it affects the resultant objects
sm.replaceFieldMakeDirty(fieldNumber, null);
storeMgr.getPersistenceHandler().updateObject(sm, new int[]{fieldNumber});
if (!relatedObjectDeleted)
{
// Mark the other object for deletion since not yet tagged
ec.deleteObjectInternal(pc);
}
}
else
{
// Can't just delete the other object since that would cause a FK constraint violation
// Do nothing - handled by DeleteRequest
NucleusLogger.DATASTORE_PERSIST.warn("Delete of " + StringUtils.toJVMIDString(sm.getObject()) +
" needs delete of related object at " + mmd.getFullFieldName() + " but cannot delete it direct since FK is here");
}
}
else
{
// We're deleting the FK at this side so shouldnt be an issue
AbstractMemberMetaData relatedMmd = mmd.getRelatedMemberMetaDataForObject(clr, sm.getObject(), pc);
if (relatedMmd != null)
{
ObjectProvider otherSM = ec.findObjectProvider(pc);
if (otherSM != null)
{
// Managed Relations : 1-1 bidir, so null out the object at the other
Object currentValue = otherSM.provideField(relatedMmd.getAbsoluteFieldNumber());
if (currentValue != null)
{
if (NucleusLogger.PERSISTENCE.isDebugEnabled())
{
NucleusLogger.PERSISTENCE.debug(LOCALISER.msg("041019",
StringUtils.toJVMIDString(pc), relatedMmd.getFullFieldName(),
sm.getObjectAsPrintable()));
}
otherSM.replaceFieldMakeDirty(relatedMmd.getAbsoluteFieldNumber(), null);
if (ec.getManageRelations())
{
otherSM.getExecutionContext().getRelationshipManager(otherSM).relationChange(
relatedMmd.getAbsoluteFieldNumber(), sm.getObject(), null);
}
}
}
}
}
}
else if (relationType == RelationType.ONE_TO_ONE_BI && mmd.getMappedBy() != null)
{
// 1-1 with FK at other side
DatastoreClass relatedTable = storeMgr.getDatastoreClass(relatedMmds[0].getClassName(), clr);
JavaTypeMapping relatedMapping = relatedTable.getMemberMapping(relatedMmds[0]);
boolean isNullable = relatedMapping.isNullable();
ObjectProvider otherSM = ec.findObjectProvider(pc);
if (dependent)
{
if (isNullable)
{
// Null out the FK in the datastore using a direct update (since we are deleting)
otherSM.replaceFieldMakeDirty(relatedMmds[0].getAbsoluteFieldNumber(), null);
storeMgr.getPersistenceHandler().updateObject(
otherSM, new int[]{relatedMmds[0].getAbsoluteFieldNumber()});
}
// Mark the other object for deletion
ec.deleteObjectInternal(pc);
}
else if (!hasFK)
{
if (isNullable())
{
Object currentRelatedValue = otherSM.provideField(relatedMmds[0].getAbsoluteFieldNumber());
if (currentRelatedValue != null)
{
// Null out the FK in the datastore using a direct update (since we are deleting)
otherSM.replaceFieldMakeDirty(relatedMmds[0].getAbsoluteFieldNumber(), null);
storeMgr.getPersistenceHandler().updateObject(
otherSM, new int[]{relatedMmds[0].getAbsoluteFieldNumber()});
// Managed Relations : 1-1 bidir, so null out the object at the other
if (ec.getManageRelations())
{
otherSM.getExecutionContext().getRelationshipManager(otherSM).relationChange(
relatedMmds[0].getAbsoluteFieldNumber(), sm.getObject(), null);
}
}
}
else
{
// TODO Remove it
}
}
else
{
// User has a FK defined (in MetaData) so let the datastore take care of it
}
}
else if (relationType == RelationType.MANY_TO_ONE_BI)
{
ObjectProvider otherSM = ec.findObjectProvider(pc);
if (relatedMmds[0].getJoinMetaData() == null)
{
// N-1 with FK at this side
if (otherSM.isDeleting())
{
// Other object is being deleted too but this side has the FK so just delete this object
}
else
{
// Other object is not being deleted so delete it if necessary
if (dependent)
{
if (isNullable())
{
// TODO Datastore nullability info can be unreliable so try to avoid this call
// Null out the FK in the datastore using a direct update (since we are deleting)
sm.replaceFieldMakeDirty(fieldNumber, null);
storeMgr.getPersistenceHandler().updateObject(sm, new int[]{fieldNumber});
}
if (ec.getApiAdapter().isDeleted(pc))
{
// Object is already tagged for deletion but we're deleting the FK so leave til flush()
}
else
{
// Mark the other object for deletion
ec.deleteObjectInternal(pc);
}
}
else
{
// Managed Relations : remove element from collection/map
if (relatedMmds[0].hasCollection())
{
// Only update the other side if not already being deleted
if (!ec.getApiAdapter().isDeleted(otherSM.getObject()) && !otherSM.isDeleting())
{
// Make sure the other object is updated in any caches
ec.markDirty(otherSM, false);
// Make sure collection field is loaded
otherSM.isLoaded(relatedMmds[0].getAbsoluteFieldNumber());
Collection otherColl = (Collection)otherSM.provideField(relatedMmds[0].getAbsoluteFieldNumber());
if (otherColl != null)
{
if (ec.getManageRelations())
{
otherSM.getExecutionContext().getRelationshipManager(otherSM).relationRemove(
relatedMmds[0].getAbsoluteFieldNumber(), sm.getObject());
}
// TODO Localise this message
NucleusLogger.PERSISTENCE.debug("ManagedRelationships : delete of object causes removal from collection at " + relatedMmds[0].getFullFieldName());
otherColl.remove(sm.getObject());
}
}
}
else if (relatedMmds[0].hasMap())
{
// TODO Cater for maps, but what is the key/value pair ?
}
}
}
}
else
{
// N-1 with join table so no FK here so need to remove from Collection/Map first? (managed relations)
if (dependent)
{
// Mark the other object for deletion
ec.deleteObjectInternal(pc);
}
else
{
// Managed Relations : remove element from collection/map
if (relatedMmds[0].hasCollection())
{
// Only update the other side if not already being deleted
if (!ec.getApiAdapter().isDeleted(otherSM.getObject()) && !otherSM.isDeleting())
{
// Make sure the other object is updated in any caches
ec.markDirty(otherSM, false);
// Make sure the other object has the collection loaded so does this change
otherSM.isLoaded(relatedMmds[0].getAbsoluteFieldNumber());
Collection otherColl = (Collection)otherSM.provideField(relatedMmds[0].getAbsoluteFieldNumber());
if (otherColl != null)
{
// TODO Localise this
NucleusLogger.PERSISTENCE.debug("ManagedRelationships : delete of object causes removal from collection at " + relatedMmds[0].getFullFieldName());
otherColl.remove(sm.getObject());
}
}
}
else if (relatedMmds[0].hasMap())
{
// TODO Cater for maps, but what is the key/value pair ?
}
}
}
}
else if (relationType == RelationType.MANY_TO_ONE_UNI)
{
// N-1 uni with join table
if (dependent)
{
// Mark the other object for deletion
ec.deleteObjectInternal(pc);
}
}
else
{
// No relation so what is this field ?