arminw
Tue, 02 Oct 2007 15:02:51 -0700
Author: arminw Date: Tue Oct 2 15:01:59 2007 New Revision: 581404 URL: http://svn.apache.org/viewvc?rev=581404&view=rev Log: improve handling of orphan objects Modified: db/ojb/branches/OJB_1_0_RELEASE/src/java/org/apache/ojb/odmg/Image.java db/ojb/branches/OJB_1_0_RELEASE/src/java/org/apache/ojb/odmg/NarrowTransaction.java db/ojb/branches/OJB_1_0_RELEASE/src/java/org/apache/ojb/odmg/ObjectEnvelope.java db/ojb/branches/OJB_1_0_RELEASE/src/java/org/apache/ojb/odmg/ObjectEnvelopeTable.java db/ojb/branches/OJB_1_0_RELEASE/src/java/org/apache/ojb/odmg/TransactionExt.java db/ojb/branches/OJB_1_0_RELEASE/src/java/org/apache/ojb/odmg/TransactionImpl.java Modified: db/ojb/branches/OJB_1_0_RELEASE/src/java/org/apache/ojb/odmg/Image.java URL: http://svn.apache.org/viewvc/db/ojb/branches/OJB_1_0_RELEASE/src/java/org/apache/ojb/odmg/Image.java?rev=581404&r1=581403&r2=581404&view=diff ============================================================================== --- db/ojb/branches/OJB_1_0_RELEASE/src/java/org/apache/ojb/odmg/Image.java (original) +++ db/ojb/branches/OJB_1_0_RELEASE/src/java/org/apache/ojb/odmg/Image.java Tue Oct 2 15:01:59 2007 @@ -206,7 +206,7 @@ */ if(!isUnmaterializedProxy() && !containsReference(oldOid)) { - listener.deletedXToN(cod, entry.getValue(), oldOid); + listener.removedXToN(cod, entry.getValue(), oldOid); } } } @@ -371,7 +371,7 @@ { if(oldOid != null) { - listener.deletedOneToOne(ord, oldRef.referenceObjOrProxy, oldOid, true); + listener.removedOneToOne(ord, oldRef.referenceObjOrProxy, oldOid, true); } } else @@ -384,7 +384,7 @@ { if(!newOid.equals(oldOid)) { - listener.deletedOneToOne(ord, oldRef.referenceObjOrProxy, oldOid, false); + listener.removedOneToOne(ord, oldRef.referenceObjOrProxy, oldOid, false); listener.addedOneToOne(ord, referenceObjOrProxy, newOid); } } @@ -481,15 +481,19 @@ //=================================================================== // inner interface //=================================================================== + /** + * This interface is used to register the detected 'new' or 'removed' objects + * when comparing the [EMAIL PROTECTED] org.apache.ojb.odmg.Image} snapshots. + */ public static interface ImageListener { public void addedOneToOne(ObjectReferenceDescriptor ord, Object refObjOrProxy, Identity oid); - public void deletedOneToOne(ObjectReferenceDescriptor ord, Object refObjOrProxy, Identity oid, boolean needsUnlink); + public void removedOneToOne(ObjectReferenceDescriptor ord, Object refObjOrProxy, Identity oid, boolean needsUnlink); public void addedXToN(CollectionDescriptor ord, Object refObjOrProxy, Identity oid); - public void deletedXToN(CollectionDescriptor ord, Object refObjOrProxy, Identity oid); + public void removedXToN(CollectionDescriptor ord, Object refObjOrProxy, Identity oid); public PersistenceBrokerInternal getBroker(); } @@ -497,7 +501,6 @@ //==================================================== // inner class //==================================================== - /** * Thrown if something unexpected is happen when handling the * object images for state detection. Modified: db/ojb/branches/OJB_1_0_RELEASE/src/java/org/apache/ojb/odmg/NarrowTransaction.java URL: http://svn.apache.org/viewvc/db/ojb/branches/OJB_1_0_RELEASE/src/java/org/apache/ojb/odmg/NarrowTransaction.java?rev=581404&r1=581403&r2=581404&view=diff ============================================================================== --- db/ojb/branches/OJB_1_0_RELEASE/src/java/org/apache/ojb/odmg/NarrowTransaction.java (original) +++ db/ojb/branches/OJB_1_0_RELEASE/src/java/org/apache/ojb/odmg/NarrowTransaction.java Tue Oct 2 15:01:59 2007 @@ -180,6 +180,17 @@ tx.setCascadingDelete(target, doCascade); } + + public void setDeleteOrphan(Class target, boolean deleteOrphan) + { + tx.setDeleteOrphan(target, deleteOrphan); + } + + public void setDeleteOrphan(Class target, String referenceField, boolean deleteOrphan) + { + tx.setDeleteOrphan(target, referenceField, deleteOrphan); + } + public boolean isOrdering() { return tx.isOrdering(); Modified: db/ojb/branches/OJB_1_0_RELEASE/src/java/org/apache/ojb/odmg/ObjectEnvelope.java URL: http://svn.apache.org/viewvc/db/ojb/branches/OJB_1_0_RELEASE/src/java/org/apache/ojb/odmg/ObjectEnvelope.java?rev=581404&r1=581403&r2=581404&view=diff ============================================================================== --- db/ojb/branches/OJB_1_0_RELEASE/src/java/org/apache/ojb/odmg/ObjectEnvelope.java (original) +++ db/ojb/branches/OJB_1_0_RELEASE/src/java/org/apache/ojb/odmg/ObjectEnvelope.java Tue Oct 2 15:01:59 2007 @@ -788,24 +788,27 @@ addLinkOneToOne(ord, false); } - public void deletedOneToOne(ObjectReferenceDescriptor ord, Object refObjOrProxy, Identity oid, boolean needsUnlink) + public void removedOneToOne(ObjectReferenceDescriptor ord, Object refObjOrProxy, Identity oid, boolean needsUnlink) { // the main objects needs link/unlink of the FK to 1:1 reference, // so mark this dirty setModificationState(modificationState.markDirty()); - ObjectEnvelope oldRefMod = buffer.getByIdentity(oid); - // only delete when the reference wasn't assigned with another object - if(!buffer.isNewAssociatedObject(oid)) - { - // if cascading delete is enabled, remove the 1:1 reference - // because it was removed from the main object - if(buffer.getTransaction().cascadeDeleteFor(ord)) - { - oldRefMod.setModificationState(oldRefMod.modificationState.markDelete()); - } - // unlink the main object - if(needsUnlink) addLinkOneToOne(ord, true); - } +// arminw: to delete a removed 1:1 reference is not the expected behavior. Only unlink the main object +// and let the reference untouched. TODO: Would it make sense to support such a behavior by configuration property? +// ObjectEnvelope oldRefMod = buffer.getByIdentity(oid); +// // only delete when the reference wasn't assigned with another object +// if(!buffer.isNewAssociatedObject(oid)) +// { +// // if delete orphan is enabled, remove the 1:1 reference +// // because it was removed from the main object +// if(buffer.getTransaction().isDeleteOrphan(ord)) +// { +// oldRefMod.setModificationState(oldRefMod.modificationState.markDelete()); +// } +// // unlink the main object +// if(needsUnlink) addLinkOneToOne(ord, true); +// } + if(needsUnlink) addLinkOneToOne(ord, true); } public void addedXToN(CollectionDescriptor cod, Object refObjOrProxy, Identity oid) @@ -847,15 +850,9 @@ // we have to link the new object mod.addLinkOneToN(cod, myObj, false); } -// arminw: this object will be matched again in ObjectEnvelopeTable#cascadingDependents() -// and then be added -// if(mod.needsInsert()) -// { -// buffer.addForInsertDependent(mod); -// } } - public void deletedXToN(CollectionDescriptor cod, Object refObjOrProxy, Identity oid) + public void removedXToN(CollectionDescriptor cod, Object refObjOrProxy, Identity oid) { ObjectEnvelope mod = buffer.getByIdentity(oid); // if this object is associated with another object it's @@ -864,13 +861,10 @@ { if(mod != null) { - boolean cascade = buffer.getTransaction().cascadeDeleteFor(cod); - if(cascade) + boolean deleteOrphan = buffer.getTransaction().isDeleteOrphan(cod); + if(deleteOrphan) { mod.setModificationState(mod.modificationState.markDelete()); - // arminw: this object will be matched again in ObjectEnvelopeTable#cascadingDependents() - // and then be added - //buffer.addForDeletionDependent(mod); } if(cod.isMtoNRelation()) { @@ -878,19 +872,19 @@ } else { - // if cascade 'true', we remove all dependent objects, so no need - // to unlink, else we have to unlink all referenced objects of this - // object - if(!cascade) + // if deleteOrphan is 'true', delete the removed object (no need + // to unlink) else we have to unlink the removed object, because it no + // longer belongs to the main object + if(!deleteOrphan) { mod.setModificationState(mod.modificationState.markDirty()); - mod.addLinkOneToN(cod, myObj, true); + mod.addLinkOneToN(cod, mod.getRealObject(), true); } } } else { - throw new Image.ImageException("Unexpected behaviour, unregistered object to delete: " + throw new Image.ImageException("Unexpected behaviour, unregistered object performed: " + oid + ", main object is " + getIdentity()+ ", envelope object is " + this.toString()); } } Modified: db/ojb/branches/OJB_1_0_RELEASE/src/java/org/apache/ojb/odmg/ObjectEnvelopeTable.java URL: http://svn.apache.org/viewvc/db/ojb/branches/OJB_1_0_RELEASE/src/java/org/apache/ojb/odmg/ObjectEnvelopeTable.java?rev=581404&r1=581403&r2=581404&view=diff ============================================================================== --- db/ojb/branches/OJB_1_0_RELEASE/src/java/org/apache/ojb/odmg/ObjectEnvelopeTable.java (original) +++ db/ojb/branches/OJB_1_0_RELEASE/src/java/org/apache/ojb/odmg/ObjectEnvelopeTable.java Tue Oct 2 15:01:59 2007 @@ -731,7 +731,7 @@ for(int i = 0; i < descriptor.size(); i++) { ObjectReferenceDescriptor ord = (ObjectReferenceDescriptor) descriptor.get(i); - if(getTransaction().cascadeDeleteFor(ord)) + if(getTransaction().isCascadeDelete(ord)) { Object depObj = ord.getPersistentField().get(source.getRealObject()); if(depObj != null) @@ -756,7 +756,7 @@ for(int i = 0; i < descriptor.size(); i++) { CollectionDescriptor col = (CollectionDescriptor) descriptor.get(i); - boolean cascadeDelete = getTransaction().cascadeDeleteFor(col); + boolean cascadeDelete = getTransaction().isCascadeDelete(col); Object collOrArray = col.getPersistentField().get(source.getRealObject()); // TODO: remove cast CollectionProxyDefaultImpl proxy = (CollectionProxyDefaultImpl) pb.getProxyFactory().getCollectionProxy(collOrArray); Modified: db/ojb/branches/OJB_1_0_RELEASE/src/java/org/apache/ojb/odmg/TransactionExt.java URL: http://svn.apache.org/viewvc/db/ojb/branches/OJB_1_0_RELEASE/src/java/org/apache/ojb/odmg/TransactionExt.java?rev=581404&r1=581403&r2=581404&view=diff ============================================================================== --- db/ojb/branches/OJB_1_0_RELEASE/src/java/org/apache/ojb/odmg/TransactionExt.java (original) +++ db/ojb/branches/OJB_1_0_RELEASE/src/java/org/apache/ojb/odmg/TransactionExt.java Tue Oct 2 15:01:59 2007 @@ -20,8 +20,8 @@ */ -import org.odmg.Transaction; import org.apache.ojb.broker.Identity; +import org.odmg.Transaction; /** * Offers useful none odmg-standard methods of the odmg [EMAIL PROTECTED] org.odmg.Transaction} interface. @@ -96,18 +96,39 @@ * * @param target The class to change cascading delete behavior of the references. * @param referenceField The field name of the 1:1, 1:n or m:n reference. - * @param doCascade If <em>true</em> cascading delete is enabled, <em>false</em> disabled. + * @param cascadeDelete If <em>true</em> cascading delete is enabled, <em>false</em> disabled. */ - public void setCascadingDelete(Class target, String referenceField, boolean doCascade); + public void setCascadingDelete(Class target, String referenceField, boolean cascadeDelete); /** * Allows to change the <em>cascading delete</em> behavior of all references of the * specified class while this transaction is in use. * * @param target The class to change cascading delete behavior of all references. - * @param doCascade If <em>true</em> cascading delete is enabled, <em>false</em> disabled. + * @param cascadeDelete If <em>true</em> cascading delete is enabled, <em>false</em> disabled. + */ + public void setCascadingDelete(Class target, boolean cascadeDelete); + + /** + * Allows to change the <em>delete orphan</em> behavior of the specified reference + * of the target class while this transaction is in use. + * + * @param target The class to change delete orphan behavior of the references. + * @param referenceField The field name of the 1:1, 1:n or 1:n reference. + * @param deleteOrphan If <em>true</em> delete orphan is enabled, <em>false</em> disabled. + */ + public void setDeleteOrphan(Class target, String referenceField, boolean deleteOrphan); + + /** + * Allows to change the <em>delete orphan</em> behavior of all 1:n and m:n references of the + * specified class while this transaction is in use - if the specified class is an + * interface, abstract class or class with "extent" classes the delete orphan flag will + * be propagated. + * + * @param target The class to change delete orphan behavior of collection references. + * @param deleteOrphan If <em>true</em> delete orphan is enabled, <em>false</em> disabled. */ - public void setCascadingDelete(Class target, boolean doCascade); + public void setDeleteOrphan(Class target, boolean deleteOrphan); /** * Return <em>true</em> if the OJB ordering algorithm is enabled. Modified: db/ojb/branches/OJB_1_0_RELEASE/src/java/org/apache/ojb/odmg/TransactionImpl.java URL: http://svn.apache.org/viewvc/db/ojb/branches/OJB_1_0_RELEASE/src/java/org/apache/ojb/odmg/TransactionImpl.java?rev=581404&r1=581403&r2=581404&view=diff ============================================================================== --- db/ojb/branches/OJB_1_0_RELEASE/src/java/org/apache/ojb/odmg/TransactionImpl.java (original) +++ db/ojb/branches/OJB_1_0_RELEASE/src/java/org/apache/ojb/odmg/TransactionImpl.java Tue Oct 2 15:01:59 2007 @@ -50,6 +50,7 @@ import org.apache.ojb.broker.util.BrokerHelper; import org.apache.ojb.broker.util.GUIDFactory; import org.apache.ojb.broker.util.IdentityArrayList; +import org.apache.ojb.broker.util.collections.IRemovalAwareCollection; import org.apache.ojb.broker.util.configuration.Configurable; import org.apache.ojb.broker.util.configuration.Configuration; import org.apache.ojb.broker.util.configuration.ConfigurationException; @@ -80,7 +81,8 @@ private final ImplementationImpl implementation; private final NamedRootsMap namedRootsMap; protected PersistenceBrokerInternal broker = null; - //private ArrayList registrationList = new ArrayList(); + private HashMap runtimeCascadeDeleteMap = new HashMap(); + private HashMap runtimeDeleteOrphanMap = new HashMap(); private boolean impliciteWriteLocks; private boolean implicitLocking; @@ -236,8 +238,7 @@ * @param obj object to acquire a lock on. * @param lockMode lock mode to acquire. The lock modes * are <code>READ</code> , <code>UPGRADE</code> , and <code>WRITE</code> . - * - * @exception LockNotGrantedException Description of Exception + * @throws LockNotGrantedException Description of Exception */ public void lock(Object obj, int lockMode) throws LockNotGrantedException { @@ -388,8 +389,7 @@ * @param oid The [EMAIL PROTECTED] org.apache.ojb.broker.Identity} of the object to lock. * @param lockMode lock mode to acquire. The lock modes * are <code>READ</code> , <code>UPGRADE</code> , and <code>WRITE</code>. - * - * @exception LockNotGrantedException Description of Exception + * @throws LockNotGrantedException Description of Exception */ void internalSingleLock(final ClassDescriptor cld, final Identity oid, final int lockMode) throws LockNotGrantedException { @@ -695,6 +695,7 @@ * Upgrade the lock on the given object to the given lock mode. Method <code> * tryLock</code> is the same as <code>lock</code> except it returns a boolean * indicating whether the lock was granted instead of generating an exception. + * * @param obj Description of Parameter * @param lockMode Description of Parameter * @return true if the lock has been acquired, otherwise false. @@ -931,6 +932,7 @@ /** * Get object by identity. First lookup among objects registered in the * transaction, then in persistent storage. + * * @param id The identity * @return The object * @throws PersistenceBrokerException @@ -1081,6 +1083,7 @@ /** * this callback is invoked before an Object is materialized * within an IndirectionHandler. + * * @param handler the invoking handler * @param oid the identity of the object to be materialized */ @@ -1094,6 +1097,7 @@ * within an IndirectionHandler. * this callback allows to defer registration of objects until * it's really neccessary. + * * @param handler the invoking handler * @param materializedObject the materialized Object */ @@ -1227,8 +1231,8 @@ } catch (PBFactoryException e) { - log.error("Cannot obtain PersistenceBroker from PersistenceBrokerFactory, " + - "found PBKey was " + curDB.getPBKey(), e); + log.error("Cannot obtain 'PersistenceBroker' instance from Database object instance: " + + curDB, e); throw new PersistenceBrokerException(e); } } @@ -1405,15 +1409,7 @@ return isTransient; } - /** - * Allows to change the <em>cascading delete</em> behavior of the specified reference - * of the target class while this transaction is in use. - * - * @param target The class to change cascading delete behavior of the references. - * @param referenceField The field name of the 1:1, 1:n or 1:n reference. - * @param doCascade If <em>true</em> cascading delete is enabled, <em>false</em> disabled. - */ - public void setCascadingDelete(Class target, String referenceField, boolean doCascade) + private ObjectReferenceDescriptor findReference(Class target, String referenceField) { ClassDescriptor cld = getBroker().getClassDescriptor(target); ObjectReferenceDescriptor ord = cld.getObjectReferenceDescriptorByName(referenceField); @@ -1423,36 +1419,23 @@ } if(ord == null) { - throw new CascadeSettingException("Invalid reference field name '" + referenceField + throw new RuntimeSettingException("Invalid reference field name '" + referenceField + "', can't find 1:1, 1:n or m:n relation with that name in " + target); } - runtimeCascadeDeleteMap.put(ord, (doCascade ? Boolean.TRUE : Boolean.FALSE)); + return ord; } - /** - * Allows to change the <em>cascading delete</em> behavior of all references of the - * specified class while this transaction is in use - if the specified class is an - * interface, abstract class or class with "extent" classes the cascading flag will - * be propagated. - * - * @param target The class to change cascading delete behavior of all references. - * @param doCascade If <em>true</em> cascading delete is enabled, <em>false</em> disabled. - */ - public void setCascadingDelete(Class target, boolean doCascade) + public void setCascadingDelete(Class target, String referenceField, boolean cascade) + { + ObjectReferenceDescriptor ord = findReference(target, referenceField); + runtimeCascadeDeleteMap.put(ord, (cascade ? Boolean.TRUE : Boolean.FALSE)); + } + + public void setCascadingDelete(Class target, boolean cascade) { ClassDescriptor cld = getBroker().getClassDescriptor(target); - List extents = cld.getExtentClasses(); - Boolean result = doCascade ? Boolean.TRUE : Boolean.FALSE; + Boolean result = cascade ? Boolean.TRUE : Boolean.FALSE; setCascadingDelete(cld, result); - if(extents != null && extents.size() > 0) - { - for(int i = 0; i < extents.size(); i++) - { - Class extent = (Class) extents.get(i); - ClassDescriptor tmp = getBroker().getClassDescriptor(extent); - setCascadingDelete(tmp, result); - } - } } private void setCascadingDelete(ClassDescriptor cld, Boolean cascade) @@ -1469,14 +1452,23 @@ Object o = collectionRefs.get(i); runtimeCascadeDeleteMap.put(o, cascade); } + if(cld.isExtent()) + { + List extents = cld.getExtentClasses(); + for(int i = 0; i < extents.size(); i++) + { + Class extent = (Class) extents.get(i); + ClassDescriptor tmp = getBroker().getClassDescriptor(extent); + setCascadingDelete(tmp, cascade); + } + } } - private HashMap runtimeCascadeDeleteMap = new HashMap(); /** * Returns <em>true</em> if cascading delete is enabled for the specified * single or collection descriptor. */ - protected boolean cascadeDeleteFor(ObjectReferenceDescriptor ord) + protected boolean isCascadeDelete(ObjectReferenceDescriptor ord) { boolean result; Boolean runtimeSetting = (Boolean) runtimeCascadeDeleteMap.get(ord); @@ -1494,6 +1486,63 @@ return result; } + + + public void setDeleteOrphan(Class target, String referenceField, boolean deleteOrphan) + { + ObjectReferenceDescriptor ord = findReference(target, referenceField); + runtimeDeleteOrphanMap.put(ord, (deleteOrphan ? Boolean.TRUE : Boolean.FALSE)); + } + + public void setDeleteOrphan(Class target, boolean deleteOrphan) + { + ClassDescriptor cld = getBroker().getClassDescriptor(target); + List extents = cld.getExtentClasses(); + Boolean result = deleteOrphan ? Boolean.TRUE : Boolean.FALSE; + setDeleteOrphan(cld, result); + if(extents != null && extents.size() > 0) + { + for(int i = 0; i < extents.size(); i++) + { + Class extent = (Class) extents.get(i); + ClassDescriptor tmp = getBroker().getClassDescriptor(extent); + setDeleteOrphan(tmp, result); + } + } + } + + private void setDeleteOrphan(ClassDescriptor cld, Boolean cascade) + { + List collectionRefs = cld.getCollectionDescriptors(true); + for(int i = 0; i < collectionRefs.size(); i++) + { + Object o = collectionRefs.get(i); + runtimeDeleteOrphanMap.put(o, cascade); + } + } + + /** + * Returns <em>true</em> if delete orphan is enabled for the specified + * single or collection descriptor. + */ + protected boolean isDeleteOrphan(ObjectReferenceDescriptor ord) + { + boolean result; + Boolean runtimeSetting = (Boolean) runtimeDeleteOrphanMap.get(ord); + if(runtimeSetting == null) + { + result = ord instanceof CollectionDescriptor && IRemovalAwareCollection.class.isAssignableFrom( + getBrokerInternal().getCollectionTypes().getCollectionClass((CollectionDescriptor) ord)); + } + else + { + result = runtimeSetting.booleanValue(); + } + return result; + } + + + int getImpliciteLockType(int parentLockMode) { return (parentLockMode == Transaction.WRITE && impliciteWriteLocks) ? Transaction.WRITE : Transaction.READ; @@ -1501,6 +1550,7 @@ /** * Return <em>true</em> if the OJB ordering algorithm is enabled. + * * @see #setOrdering(boolean) */ public boolean isOrdering() @@ -1563,25 +1613,25 @@ // inner class //============================================================ /** - * This was thrown when something wrong with the cascading delete setting. + * This was thrown when something wrong with the runtime setting. */ - static class CascadeSettingException extends OJBRuntimeException + static class RuntimeSettingException extends OJBRuntimeException { - public CascadeSettingException() + public RuntimeSettingException() { } - public CascadeSettingException(String msg) + public RuntimeSettingException(String msg) { super(msg); } - public CascadeSettingException(Throwable cause) + public RuntimeSettingException(Throwable cause) { super(cause); } - public CascadeSettingException(String msg, Throwable cause) + public RuntimeSettingException(String msg, Throwable cause) { super(msg, cause); } --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]