You are viewing a plain text version of this content. The canonical link for it is here.
Posted to ojb-dev@db.apache.org by ar...@apache.org on 2006/07/15 16:28:05 UTC

svn commit: r422235 [1/3] - in /db/ojb/trunk/src: java/org/apache/ojb/odmg/ java/org/apache/ojb/odmg/oql/ java/org/apache/ojb/odmg/states/ java/org/odmg/ jdori/org/apache/ojb/jdori/sql/ samples/org/apache/ojb/tutorials/

Author: arminw
Date: Sat Jul 15 07:28:03 2006
New Revision: 422235

URL: http://svn.apache.org/viewvc?rev=422235&view=rev
Log:
merge trunk with 1.0.x

Modified:
    db/ojb/trunk/src/java/org/apache/ojb/odmg/NamedRootsMap.java
    db/ojb/trunk/src/java/org/apache/ojb/odmg/NarrowTransaction.java
    db/ojb/trunk/src/java/org/apache/ojb/odmg/ObjectEnvelope.java
    db/ojb/trunk/src/java/org/apache/ojb/odmg/ObjectEnvelopeOrdering.java
    db/ojb/trunk/src/java/org/apache/ojb/odmg/ObjectEnvelopeTable.java
    db/ojb/trunk/src/java/org/apache/ojb/odmg/PBCapsule.java
    db/ojb/trunk/src/java/org/apache/ojb/odmg/RuntimeObject.java
    db/ojb/trunk/src/java/org/apache/ojb/odmg/TransactionAbortedExceptionOJB.java
    db/ojb/trunk/src/java/org/apache/ojb/odmg/TransactionAware.java
    db/ojb/trunk/src/java/org/apache/ojb/odmg/TransactionExt.java
    db/ojb/trunk/src/java/org/apache/ojb/odmg/TransactionImpl.java
    db/ojb/trunk/src/java/org/apache/ojb/odmg/TxUtil.java
    db/ojb/trunk/src/java/org/apache/ojb/odmg/oql/OQLQueryImpl.java
    db/ojb/trunk/src/java/org/apache/ojb/odmg/states/ModificationState.java
    db/ojb/trunk/src/java/org/apache/ojb/odmg/states/StateNewClean.java
    db/ojb/trunk/src/java/org/apache/ojb/odmg/states/StateNewDelete.java
    db/ojb/trunk/src/java/org/apache/ojb/odmg/states/StateNewDirty.java
    db/ojb/trunk/src/java/org/apache/ojb/odmg/states/StateOldClean.java
    db/ojb/trunk/src/java/org/apache/ojb/odmg/states/StateOldDelete.java
    db/ojb/trunk/src/java/org/apache/ojb/odmg/states/StateOldDirty.java
    db/ojb/trunk/src/java/org/apache/ojb/odmg/states/StateTransient.java
    db/ojb/trunk/src/java/org/odmg/Implementation.java
    db/ojb/trunk/src/jdori/org/apache/ojb/jdori/sql/OjbStoreManager.java
    db/ojb/trunk/src/samples/org/apache/ojb/tutorials/PBExample.java

Modified: db/ojb/trunk/src/java/org/apache/ojb/odmg/NamedRootsMap.java
URL: http://svn.apache.org/viewvc/db/ojb/trunk/src/java/org/apache/ojb/odmg/NamedRootsMap.java?rev=422235&r1=422234&r2=422235&view=diff
==============================================================================
--- db/ojb/trunk/src/java/org/apache/ojb/odmg/NamedRootsMap.java (original)
+++ db/ojb/trunk/src/java/org/apache/ojb/odmg/NamedRootsMap.java Sat Jul 15 07:28:03 2006
@@ -1,6 +1,6 @@
 package org.apache.ojb.odmg;
 
-/* Copyright 2002-2004 The Apache Software Foundation
+/* Copyright 2002-2005 The Apache Software Foundation
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -28,6 +28,7 @@
 import org.apache.ojb.broker.metadata.ClassDescriptor;
 import org.apache.ojb.broker.util.logging.Logger;
 import org.apache.ojb.broker.util.logging.LoggerFactory;
+import org.apache.ojb.broker.util.ObjectModification;
 import org.odmg.ClassNotPersistenceCapableException;
 import org.odmg.ObjectNameNotFoundException;
 import org.odmg.ObjectNameNotUniqueException;
@@ -40,14 +41,13 @@
  * therefore the NamedRootsMap underlies the same transaction management
  * as all other persistent objects
  *
- * @author Thomas Mahler
  * @version $Id$
  */
 public class NamedRootsMap
 {
     private Logger log = LoggerFactory.getLogger(NamedRootsMap.class);
-    private TransactionImpl tx;
-    private HashMap tempBindings;
+    private final TransactionImpl tx;
+    private final HashMap tempBindings;
     private Map deletionMap;
     private Map insertMap;
 
@@ -77,6 +77,16 @@
     }
 
     /**
+     * Returns <em>true</em> if this class manage objects
+     * to persist/delete in datastore.
+     */
+    public boolean needsCommit()
+    {
+        return (insertMap != null && insertMap.size() > 0)
+            || (deletionMap != null && deletionMap.size() > 0);
+    }
+
+    /**
      * Have to be performed after the "normal" objects be written
      * to DB and before method {@link #performInsert()}.
      */
@@ -108,15 +118,16 @@
             {
                 NamedEntry namedEntry = (NamedEntry) it.next();
                 namedEntry.prepareForStore(broker);
-                broker.store(namedEntry, org.apache.ojb.broker.OJB.INSERT);
+                broker.store(namedEntry, ObjectModification.INSERT);
             }
         }
     }
 
-    public void afterWriteCleanup()
+    public void cleanup()
     {
-        if(deletionMap != null) deletionMap.clear();
-        if(insertMap != null) insertMap.clear();
+        if(deletionMap != null && deletionMap.size() > 0) deletionMap.clear();
+        if(insertMap != null && insertMap.size() > 0) insertMap.clear();
+        if(tempBindings.size() > 0) insertMap.clear();
     }
 
     private void localBind(String key, NamedEntry entry) throws ObjectNameNotUniqueException
@@ -178,7 +189,7 @@
                 result = tx.getBroker().getObjectByIdentity(objectIdentity);
                 // lock the persistance capable object
                 RuntimeObject rt = new RuntimeObject(result, objectIdentity, tx, false);
-                tx.lockAndRegister(rt, Transaction.READ, tx.getRegistrationList());
+                tx.lockAndRegister(rt, Transaction.READ);
             }
             else
             {
@@ -215,8 +226,9 @@
         {
             cld = broker.getClassDescriptor(broker.getProxyFactory().getRealClass(object));
         }
-        catch(PersistenceBrokerException e)
+        catch(PersistenceBrokerException ignore)
         {
+            // ignore
         }
 
         // if null a non-persistent capable object was specified
@@ -238,7 +250,7 @@
             // else persist the specified named object
             if(!rt.isNew())
             {
-                tx.lockAndRegister(rt, Transaction.READ, tx.getRegistrationList());
+                tx.lockAndRegister(rt, Transaction.READ);
             }
             else
             {

Modified: db/ojb/trunk/src/java/org/apache/ojb/odmg/NarrowTransaction.java
URL: http://svn.apache.org/viewvc/db/ojb/trunk/src/java/org/apache/ojb/odmg/NarrowTransaction.java?rev=422235&r1=422234&r2=422235&view=diff
==============================================================================
--- db/ojb/trunk/src/java/org/apache/ojb/odmg/NarrowTransaction.java (original)
+++ db/ojb/trunk/src/java/org/apache/ojb/odmg/NarrowTransaction.java Sat Jul 15 07:28:03 2006
@@ -1,10 +1,5 @@
 package org.apache.ojb.odmg;
 
-import org.apache.ojb.broker.Identity;
-import org.apache.ojb.broker.PersistenceBroker;
-import org.apache.ojb.broker.PersistenceBrokerException;
-import org.odmg.LockNotGrantedException;
-
 /* Copyright 2002-2004 The Apache Software Foundation
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,6 +15,11 @@
  * limitations under the License.
  */
 
+import org.odmg.LockNotGrantedException;
+import org.apache.ojb.broker.PersistenceBroker;
+import org.apache.ojb.broker.Identity;
+import org.apache.ojb.broker.PersistenceBrokerException;
+
 /**
  * Wraps {@link org.odmg.Transaction} in managed environments.
  *
@@ -185,15 +185,15 @@
         tx.setOrdering(ordering);
     }
 
-    public boolean isNoteUserOrder()
-    {
-        return tx.isNoteUserOrder();
-    }
-
-    public void setNoteUserOrder(boolean noteUserOrder)
-    {
-        tx.setNoteUserOrder(noteUserOrder);
-    }
+//    public boolean isNoteUserOrder()
+//    {
+//        return tx.isNoteUserOrder();
+//    }
+//
+//    public void setNoteUserOrder(boolean noteUserOrder)
+//    {
+//        tx.setNoteUserOrder(noteUserOrder);
+//    }
 
     public boolean isDeleted(Identity id)
     {

Modified: db/ojb/trunk/src/java/org/apache/ojb/odmg/ObjectEnvelope.java
URL: http://svn.apache.org/viewvc/db/ojb/trunk/src/java/org/apache/ojb/odmg/ObjectEnvelope.java?rev=422235&r1=422234&r2=422235&view=diff
==============================================================================
--- db/ojb/trunk/src/java/org/apache/ojb/odmg/ObjectEnvelope.java (original)
+++ db/ojb/trunk/src/java/org/apache/ojb/odmg/ObjectEnvelope.java Sat Jul 15 07:28:03 2006
@@ -1,6 +1,6 @@
 package org.apache.ojb.odmg;
 
-/* Copyright 2002-2004 The Apache Software Foundation
+/* Copyright 2002-2005 The Apache Software Foundation
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,51 +14,43 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-/**
- *
- * @author <a href="mailto:thma@apache.org">Thomas Mahler</a>
- * @author <a href="mailto:mattbaird@yahoo.com">Matthew Baird</a>
- *
- */
 
-import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Iterator;
-import java.util.List;
 import java.util.Map;
+import java.util.List;
+import java.util.ArrayList;
 
 import org.apache.commons.lang.builder.ToStringBuilder;
 import org.apache.ojb.broker.Identity;
-import org.apache.ojb.broker.OJBRuntimeException;
 import org.apache.ojb.broker.PersistenceBroker;
 import org.apache.ojb.broker.PersistenceBrokerException;
+import org.apache.ojb.broker.OJBRuntimeException;
 import org.apache.ojb.broker.PersistenceBrokerInternal;
-import org.apache.ojb.broker.core.proxy.CollectionProxy;
-import org.apache.ojb.broker.core.proxy.CollectionProxyListener;
 import org.apache.ojb.broker.core.proxy.IndirectionHandler;
+import org.apache.ojb.broker.core.proxy.ProxyFactory;
 import org.apache.ojb.broker.metadata.ClassDescriptor;
 import org.apache.ojb.broker.metadata.CollectionDescriptor;
 import org.apache.ojb.broker.metadata.FieldDescriptor;
-import org.apache.ojb.broker.metadata.FieldType;
 import org.apache.ojb.broker.metadata.ObjectReferenceDescriptor;
 import org.apache.ojb.broker.util.BrokerHelper;
 import org.apache.ojb.broker.util.ObjectModification;
 import org.apache.ojb.broker.util.logging.Logger;
 import org.apache.ojb.broker.util.logging.LoggerFactory;
-import org.apache.ojb.odmg.link.LinkEntry;
-import org.apache.ojb.odmg.link.LinkEntryOneToN;
-import org.apache.ojb.odmg.link.LinkEntryOneToOne;
 import org.apache.ojb.odmg.states.ModificationState;
 import org.apache.ojb.odmg.states.StateNewDirty;
 import org.apache.ojb.odmg.states.StateOldClean;
 import org.apache.ojb.odmg.states.StateOldDirty;
+import org.apache.ojb.odmg.link.LinkEntry;
+import org.apache.ojb.odmg.link.LinkEntryOneToOne;
+import org.apache.ojb.odmg.link.LinkEntryOneToN;
 
 /**
  * ObjectEnvelope is used during ODMG transactions as a wrapper for a
  * persistent objects declaration
  *
  */
-public class ObjectEnvelope implements ObjectModification
+public class ObjectEnvelope implements ObjectModification, Image.ImageListener
 {
     private Logger log = LoggerFactory.getLogger(ObjectEnvelope.class);
 
@@ -75,6 +67,8 @@
     private Identity oid;
     private Boolean hasChanged;
     private boolean writeLocked;
+    private boolean markedForCascadingInsert;
+    private boolean markedForCascadingDelete;
 
     /**
      * myObj holds the object we are wrapping.
@@ -84,7 +78,7 @@
     /**
      * beforeImage holds a mapping between field
      * names and values at the start of the transaction.
-     * afterImage holds the mapping at the
+     * currentImage holds the mapping at the
      * end of the transaction.
      */
     private Map beforeImage;
@@ -94,21 +88,22 @@
     private List linkEntryList;
 
     /**
-     *
      * Create a wrapper by providing an Object.
      */
-    public ObjectEnvelope(ObjectEnvelopeTable buffer, Identity oid, Object obj, boolean isNewObject)
+    ObjectEnvelope(ObjectEnvelopeTable buffer, Identity oid, Object obj, boolean isNewObject)
     {
         this.linkEntryList = new ArrayList();
         this.buffer = buffer;
         this.oid = oid;
         // TODO: do we really need to materialize??
-        myObj = buffer.getTransaction().getBrokerInternal().getProxyFactory().getRealObject(obj);
-        /*
-        if object is new don't make an image, because we know it needs insert
-        */
-        if(!isNewObject) refreshObjectImage();
+        myObj = getProxyFactory().getRealObject(obj);
         prepareInitialState(isNewObject);
+        beforeImage = buildObjectImage(getBroker(), isNewObject);
+    }
+
+    private ProxyFactory getProxyFactory()
+    {
+        return buffer.getTransaction().getBrokerInternal().getProxyFactory();
     }
 
     public PersistenceBrokerInternal getBroker()
@@ -126,7 +121,7 @@
         return buffer;
     }
 
-    public Map getBeforeImage()
+    private Map getBeforeImage()
     {
         if(beforeImage == null)
         {
@@ -135,56 +130,67 @@
         return beforeImage;
     }
 
-    public Map getCurrentImage()
+    private Map getCurrentImage()
     {
         if(currentImage == null)
         {
-            currentImage = buildObjectImage(getBroker());
+            currentImage = buildObjectImage(getBroker(), needsInsert());
         }
         return currentImage;
     }
 
     /**
-     * This method should be called before commit method/ transaction ends
+     * This method should be called before transaction ends
      * to allow cleanup of used resources, e.g. remove proxy listener objects
      * to avoid invoke of registered objects after tx end.
      */
-    public void cleanup(boolean reuse)
+    public void cleanup(boolean reuse, boolean wasInsert)
     {
         if(currentImage != null)
         {
-            Iterator iterator = currentImage.values().iterator();
-            while(iterator.hasNext())
-            {
-                EqualsBase base =  (EqualsBase) iterator.next();
-                if(base != null) base.cleanup(reuse);
-            }
+            performImageCleanup(currentImage, reuse);
         }
         if(beforeImage != null)
         {
-            Iterator iterator = beforeImage.values().iterator();
-            while(iterator.hasNext())
-            {
-                EqualsBase base =  (EqualsBase) iterator.next();
-                // we always free resources of the old image
-                if(base != null) base.cleanup(false);
-            }
+            // we always free all resources of the old image
+            performImageCleanup(beforeImage, false);
+        }
+        if(reuse)
+        {
+            refreshObjectImage(wasInsert);
+            // on reuse of this object, we have to clear the link list
+            if(linkEntryList.size() > 0) linkEntryList.clear();
+        }
+        else
+        {
+            myObj = null;
         }
-        if(!reuse) myObj = null;
     }
 
-    public void refreshObjectImage()
+    private void performImageCleanup(Map imageMap, boolean reuse)
+    {
+        Iterator iterator = imageMap.values().iterator();
+        while(iterator.hasNext())
+        {
+            Image base =  (Image) iterator.next();
+            if(base != null) base.cleanup(reuse);
+        }
+    }
+
+    private void refreshObjectImage(boolean wasNewObject)
     {
-        PersistenceBroker broker = getBroker();
         try
         {
+            markedForCascadingInsert = false;
+            markedForCascadingDelete = false;
+            
             // if an image already exists we
             // replace the Identity too, maybe a temporary
             // used PK value was replaced by the real one,
             // see in docs SequenceManagerNativeImpl
-            if(beforeImage != null)
+            if(getIdentity().isTransient())
             {
-                oid = broker.serviceIdentity().buildIdentity(myObj);
+                refreshIdentity();
             }
             if(currentImage != null)
             {
@@ -194,24 +200,75 @@
             {
                 if(beforeImage == null)
                 {
-                    beforeImage = buildObjectImage(getBroker());
+                    beforeImage = buildObjectImage(getBroker(), wasNewObject);
                 }
             }
             currentImage = null;
             hasChanged = null;
+            if(wasNewObject)
+            {
+                /*
+                on insert we have to replace the PK fields and the version fields, because
+                they populated after the object was written to DB, thus replace all field image values
+                */
+                // refreshPKFields();
+                buildImageForFields(beforeImage, getClassDescriptor());
+            }
+            // TODO: How to handle version fields incremented by the DB?
+            // always refresh the version fields, because these fields will change when written to DB
+            refreshLockingFields();
         }
-        catch(Exception ex)
+        catch(PersistenceBrokerException e)
         {
             beforeImage = null;
             currentImage = null;
             hasChanged = null;
-            log.error("Can't refresh object image", ex);
-            throw new org.odmg.ClassNotPersistenceCapableException(ex.toString());
+            log.error("Can't refresh object image for " + getIdentity(), e);
+            throw e;
         }
     }
 
+// TODO: Is it possible that PK fields change? If a FK is part of a coumpound PK wouldn't it possible?
+//    private void refreshPKFields()
+//    {
+//        FieldDescriptor[] flds = getClassDescriptor().getPkFields();
+//        for(int i = 0; i < flds.length; i++)
+//        {
+//            FieldDescriptor fld = flds[i];
+//            addFieldImage(beforeImage, fld);
+//        }
+//    }
+
+    private void refreshLockingFields()
+    {
+        if(getClassDescriptor().isLocking())
+        {
+            FieldDescriptor[] flds = getClassDescriptor().getLockingFields();
+            for(int i = 0; i < flds.length; i++)
+            {
+                FieldDescriptor fld = flds[i];
+                addFieldImage(beforeImage, fld);
+            }
+        }
+    }
+
+    /**
+     * Replace the current with a new generated identity object and
+     * returns the old one.
+     */
+    public Identity refreshIdentity()
+    {
+        Identity oldOid = getIdentity();
+        this.oid = getBroker().serviceIdentity().buildIdentity(myObj);
+        return oldOid;
+    }
+
     public Identity getIdentity()
     {
+        if(oid == null)
+        {
+            oid = getBroker().serviceIdentity().buildIdentity(getObject());
+        }
         return oid;
     }
 
@@ -223,6 +280,11 @@
         return myObj;
     }
 
+    public Object getRealObject()
+    {
+        return getProxyFactory().getRealObject(myObj);
+    }
+
     public void refreshObjectIfNeeded(Object obj)
     {
         if(this.myObj != obj)
@@ -297,117 +359,95 @@
     /**
      * buildObjectImage() will return the image of the Object.
      */
-    private Map buildObjectImage(PersistenceBrokerInternal broker) throws PersistenceBrokerException
+    private Map buildObjectImage(PersistenceBroker broker) throws PersistenceBrokerException
     {
-        Map fieldValues = new HashMap();
-        ClassDescriptor mif = broker.getClassDescriptor(getObject().getClass());
+        return buildObjectImage(broker, false);
+    }
+
+    private Map buildObjectImage(PersistenceBroker broker, boolean isNew) throws PersistenceBrokerException
+    {
+        Map imageMap = new HashMap();
+        ClassDescriptor cld = broker.getClassDescriptor(getObject().getClass());
         //System.out.println("++++ build image: " + getObject());
+        // register 1:1 references in image
+        buildImageForSingleReferences(imageMap, cld);
+        // put object values to image map, skip this for new objects
+        if(!isNew) buildImageForFields(imageMap, cld);
+        // register 1:n and m:n references in image
+        buildImageForCollectionReferences(imageMap, cld);
+        return imageMap;
+    }
 
-        /**
-         * MBAIRD
-         * 1. register all 1:1 references
-         * field changes to 1:1 mapped objects should also be registered in the map,
-         * so that alterations to those related objects will trigger an object to be
-         * marked "dirty", otherwise attaching or detaching a 1:1 referenced object will
-         * not be updated in ODMG.
-         */
-        Iterator iter = mif.getObjectReferenceDescriptors(true).iterator();
+    private void buildImageForSingleReferences(Map imageMap, ClassDescriptor cld)
+    {
+        // register all 1:1 references
+        Iterator iter = cld.getObjectReferenceDescriptors(true).iterator();
         ObjectReferenceDescriptor rds;
         while(iter.hasNext())
         {
-            Object referenceObject;
-            EqualsRefHelper erh;
             rds = (ObjectReferenceDescriptor) iter.next();
-
-            /*
-             synchronize on myObj so the ODMG-layer can take a snapshot only of
-             fully cached (i.e. with all references + collections) objects
-             arminw:
-             The PB-api only return full materialized objects, thus it's not
-             needed to sync.
-             */
-//            synchronized(myObj)
-//            {
-//                referenceObject = rds.getPersistentField().get(myObj);
-//            }
-            referenceObject = rds.getPersistentField().get(myObj);
-
-            /**
-             * MBAIRD
-             * In the case of a proxy, we check if it has been materialized
-             * if it's been materialized, we put it in the map, because it could change.
-             * if it hasn't been materialized, it hasn't changed.
-             *
-             * Also handles virtual proxies.
-             *
-             * arminw:
-             * wrap Object or Identity with a helper class. The main object will get
-             * dirty when the 1:1 reference change: add or replaced by another object or deleted
-             */
-            IndirectionHandler handler = broker.getProxyFactory().getIndirectionHandler(referenceObject);
-            // if it is a not materialized proxy, use the Identity
-            if(handler != null)
-            {
-                erh = handler.alreadyMaterialized()
-                        ? new EqualsRefHelper(handler.getRealSubject())
-                        : new EqualsRefHelper(handler.getIdentity());
-            }
-            else
-            {
             /*
             arminw:
-            if object was serialized and anonymous FK are used in the main object, the FK
-            values are null, we have to refresh (re-assign) this values before building field images
+            if a "super-reference" is matched (a 1:1 reference used to represent a super class)
+            we don't handle it, because this will be done by the PB-api and will never be change
             */
-                if(referenceObject != null
-                        && BrokerHelper.hasAnonymousKeyReference(rds.getClassDescriptor(), rds))
+            if(!rds.isSuperReferenceDescriptor())
             {
-                getBroker().serviceBrokerHelper().link(myObj, rds, false);
-            }
-                erh = new EqualsRefHelper(referenceObject);
+                Object referenceObject = rds.getPersistentField().get(myObj);
+
+                IndirectionHandler handler = getProxyFactory().getIndirectionHandler(referenceObject);
+                /*
+                arminw:
+                if object was serialized and anonymous FK are used in the main object, the FK
+                values are null, we have to refresh (re-assign) these values before building field images.
+                This will not touch the main object itself, because we only reassign anonymous FK fields.
+                */
+                if(handler == null && referenceObject != null
+                        && BrokerHelper.hasAnonymousKeyReference(rds.getClassDescriptor(), rds))
+                {
+                    getBroker().serviceBrokerHelper().link(myObj, rds, false);
+                }
+                Image.SingleRef singleRef = new Image.SingleRef(this, rds, referenceObject);
+                imageMap.put(rds, singleRef);
             }
-            /*
-             register the Identity for 1:1 relations only, if change we have
-             to update the main object
-             */
-            fieldValues.put(rds, erh);
         }
+    }
 
-
-        /**
-         * MBAIRD
-         * 2. register all fields of object (with inherited fields) that aren't collections or references
-         */
-        FieldDescriptor[] fieldDescs = mif.getFieldDescriptor(true);
+    private void buildImageForFields(Map imageMap, ClassDescriptor cld)
+    {
+        // register all non reference fields of object (with inherited fields)
+        FieldDescriptor[] fieldDescs = cld.getFieldDescriptor(true);
         for(int i = 0; i < fieldDescs.length; i++)
         {
-            FieldDescriptor fld = fieldDescs[i];
-            // map copies of all field values
-            Object value = fld.getPersistentField().get(myObj);
-            // get the real sql type value
-            value = fld.getFieldConversion().javaToSql(value);
-            // make copy of the sql type value
-            value = fld.getJdbcType().getFieldType().copy(value);
-            // buffer in image the field name and the sql type value
-            // wrapped by a helper class
-            fieldValues.put(fld.getPersistentField().getName(), new EqualsFieldHelper(fld.getJdbcType().getFieldType(), value));
+            addFieldImage(imageMap, fieldDescs[i]);
         }
+    }
 
+    private void addFieldImage(Map imageMap, FieldDescriptor fld)
+    {
+        // register copies of all field values
+        Object value = fld.getPersistentField().get(myObj);
+        // get the real sql type value
+        value = fld.getFieldConversion().javaToSql(value);
+        // make copy of the sql type value
+        value = fld.getJdbcType().getFieldType().copy(value);
+        // buffer in image the field name and the sql type value
+        // wrapped by a helper class
+        imageMap.put(fld.getPersistentField().getName(), new Image.Field(fld.getJdbcType().getFieldType(), value));
+    }
 
-        /**
-         * MBAIRD
-         * 3. now let's register the collection descriptors
-         */
-        Iterator collections = mif.getCollectionDescriptors(true).iterator();
+    private void buildImageForCollectionReferences(Map imageMap, ClassDescriptor cld)
+    {
+        // register the 1:n and m:n references
+        Iterator collections = cld.getCollectionDescriptors(true).iterator();
         CollectionDescriptor cds;
         while(collections.hasNext())
         {
             cds = (CollectionDescriptor) collections.next();
             Object collectionOrArray = cds.getPersistentField().get(myObj);
-            EqualsColHelper ech = new EqualsColHelper(cds, collectionOrArray);
-            fieldValues.put(cds, ech);
+            Image.MultipleRef colRef = new Image.MultipleRef(this, cds, collectionOrArray);
+            imageMap.put(cds, colRef);
         }
-        return fieldValues;
     }
 
     /**
@@ -471,7 +511,7 @@
             initialState = StateOldClean.getInstance();
         }
         // remember it:
-        modificationState = initialState;
+        this.modificationState = initialState;
     }
 
     /**
@@ -494,18 +534,18 @@
      */
     public void setModificationState(ModificationState newModificationState)
     {
-        if(newModificationState != modificationState)
+        if(newModificationState != this.modificationState)
         {
             if(log.isDebugEnabled())
             {
                 log.debug("object state transition for object " + this.oid + " ("
-                        + modificationState + " --> " + newModificationState + ")");
+                        + this.modificationState + " --> " + newModificationState + ")");
 //                try{throw new Exception();}catch(Exception e)
 //                {
 //                e.printStackTrace();
 //                }
             }
-            modificationState = newModificationState;
+            this.modificationState = newModificationState;
         }
     }
 
@@ -517,7 +557,7 @@
     {
         ToStringBuilder buf = new ToStringBuilder(this);
         buf.append("Identity", oid)
-            .append("ModificationState", modificationState.toString());
+            .append("ModificationState", (modificationState != null ? modificationState.toString() : "invalid"));
         return buf.toString();
     }
 
@@ -531,17 +571,42 @@
      */
     public boolean hasChanged(PersistenceBroker broker)
     {
-        try
-        {
-            currentImage = getCurrentImage();
-        }
-        catch(Exception e)
-        {
-            log.warn("Could not verify object changes, return hasChanged 'true'", e);
-        }
         if(hasChanged == null)
         {
-            hasChanged = (beforeImage != null && beforeImage.equals(currentImage) ? Boolean.FALSE : Boolean.TRUE);
+            Map current = null;
+            try
+            {
+                current = getCurrentImage();
+            }
+            catch(Exception e)
+            {
+                log.warn("Could not verify object changes, mark dirty: " + getIdentity(), e);
+            }
+            if(beforeImage != null && current != null)
+            {
+                Iterator it = beforeImage.entrySet().iterator();
+                hasChanged = Boolean.FALSE;
+                while(it.hasNext())
+                {
+                    Map.Entry entry =  (Map.Entry) it.next();
+                    Image imageBefore = (Image) entry.getValue();
+                    Image imageCurrent = (Image) current.get(entry.getKey());
+                    if(imageBefore.modified(imageCurrent))
+                    {
+                        hasChanged = Boolean.TRUE;
+                        break;
+                    }
+                }
+            }
+            else
+            {
+                hasChanged = Boolean.TRUE;
+            }
+            if(log.isDebugEnabled())
+            {
+                log.debug("State detection for " + getIdentity() + " --> object "
+                        + (hasChanged.booleanValue() ? "has changed" : "unchanged"));
+            }
         }
         return hasChanged.booleanValue();
     }
@@ -553,7 +618,7 @@
     void markReferenceElements(PersistenceBroker broker)
     {
         // these cases will be handled by ObjectEnvelopeTable#cascadingDependents()
-        if(getModificationState().needsInsert() || getModificationState().needsDelete()) return;
+        // if(getModificationState().needsInsert() || getModificationState().needsDelete()) return;
 
         Map oldImage = getBeforeImage();
         Map newImage = getCurrentImage();
@@ -562,193 +627,13 @@
         while (iter.hasNext())
         {
             Map.Entry entry = (Map.Entry) iter.next();
-            // CollectionDescriptor extends ObjectReferenceDescriptor
-            // we have to search for XXXDescriptor
-            if(entry.getKey() instanceof ObjectReferenceDescriptor)
-            {
-                if (entry.getKey() instanceof CollectionDescriptor)
-                {
-                    CollectionDescriptor cds = (CollectionDescriptor) entry.getKey();
-                    EqualsColHelper oldEch = (EqualsColHelper) oldImage.get(cds);
-                    EqualsColHelper newEch = (EqualsColHelper) entry.getValue();
-//System.out.println("");
-//System.out.println("mark-oldEch: " + oldEch);
-//System.out.println("mark-newEch: " + newEch);
-
-                    markDelete(cds, oldEch, newEch);
-                    markNew(cds, oldEch, newEch);
-                }
-                else
-                {
-                    /*
-                    check for new 1:1 reference object
-                    */
-                    ObjectReferenceDescriptor rds = (ObjectReferenceDescriptor) entry.getKey();
-                    EqualsRefHelper oldEh = (EqualsRefHelper) oldImage.get(rds);
-                    EqualsRefHelper newEh = (EqualsRefHelper) newImage.get(rds);
-                    if(!oldEh.equals(newEh))
-                    {
-                        // the main objects needs link/unlink of the FK to 1:1 reference,
-                        // so mark this dirty
-                        setModificationState(getModificationState().markDirty());
-                        // if the new reference helper value is not null
-                        // Lock the object, because it can be a new unregistered object.
-                        // in other cases it can be an unmaterialized proxy and we
-                        // don't need to materialize
-                        if(newEh.value != null)
-                        {
-                            // if the object is already registered, OJB knows about
-                            // else lock and register object, get read lock, because we
-                            // don't know if the object is new or moved from an existing other object
-                            ObjectEnvelope oe = buffer.getByIdentity(newEh.oid);
-                            if(oe == null)
-                            {
-                                RuntimeObject rt = new RuntimeObject(newEh.value, getTx());
-                                getTx().lockAndRegister(rt, TransactionExt.READ, false, getTx().getRegistrationList());
-                            }
-                            // in any case we need to link the main object
-                            addLinkOneToOne(rds, false);
-                        }
-                        // if the new image doesn't contain a reference object, the
-                        // reference is deleted, so lookup the old reference object
-                        // and mark for delete
-                        else if(newEh.isNull())
-                        {
-                            ObjectEnvelope oldRefMod = buffer.getByIdentity(oldEh.oid);
-                            // only delete when the reference wasn't assigned with another object
-                            if(!buffer.isNewAssociatedObject(oldEh.oid))
-                            {
-                                // if cascading delete is enabled, remove the 1:1 reference
-                                // because it was removed from the main object
-                                if(buffer.getTransaction().cascadeDeleteFor(rds))
-                                {
-                                    oldRefMod.setModificationState(oldRefMod.getModificationState().markDelete());
-                                }
-                                // in any case we have to unlink the main object
-                                addLinkOneToOne(rds, true);
-                            }
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * Mark object for delete if it's not available in the new Collection.
-     */
-    private void markDelete(CollectionDescriptor cds, EqualsColHelper oldEch, EqualsColHelper newEch)
-    {
-        if (oldEch.references.size() == 0)
-        {
-            return;
-        }
-        Iterator oldIter = oldEch.references.entrySet().iterator();
-        Map newRefs = newEch.references;
-        while (oldIter.hasNext())
-        {
-            Map.Entry entry = (Map.Entry) oldIter.next();
-            Identity oldOid = (Identity) entry.getKey();
-            if (!newRefs.containsKey(oldOid) && newEch.status != IS_UNMATERIALIZED_PROXY)
-            {
-                ObjectEnvelope mod = buffer.getByIdentity(oldOid);
-                // if this object is associated with another object it's
-                // not allowed to remove it
-                if(!buffer.isNewAssociatedObject(oldOid))
-                {
-                    if(mod != null)
-                    {
-                        boolean cascade = buffer.getTransaction().cascadeDeleteFor(cds);
-                        if(cascade)
-                        {
-                            mod.setModificationState(mod.getModificationState().markDelete());
-                            buffer.addForDeletionDependent(mod);
-                        }
-                        if(cds.isMtoNRelation())
-                        {
-                            buffer.addM2NUnlinkEntry(cds, getObject(), entry.getValue());
-                        }
-                        else
-                        {
-                            // when cascade 'true' we remove all dependent objects, so no need to unlink
-                            if(!cascade)
-                            {
-                                mod.setModificationState(mod.getModificationState().markDirty());
-                                mod.addLinkOneToN(cds, getObject(), true);
-                            }
-                        }
-                    }
-                    else
-                    {
-                        throw new ImageExcetion("Unexpected behaviour, unregistered object to delete: "
-                                + oldOid + ", main object is " + getIdentity());
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * Mark object for insert if it's not available in the old Collection.
-     */
-    private void markNew(CollectionDescriptor cds, EqualsColHelper oldEch, EqualsColHelper newEch)
-    {
-        if (newEch.references.size() == 0)
-        {
-            return;
-        }
-
-        Iterator newIter = newEch.references.entrySet().iterator();
-        Map oldRefs = oldEch.references;
-        while (newIter.hasNext())
-        {
-            Map.Entry entry = (Map.Entry) newIter.next();
-            Identity newOid = (Identity) entry.getKey();
-            Object newObj = entry.getValue();
-
-            /*
-            search for new objects: if in the old reference collection an object
-            of the new reference collection is not contained
-            */
-            if ((oldRefs == null) || !oldRefs.containsKey(newOid))
-            {
-                ObjectEnvelope mod = buffer.getByIdentity(newOid);
-                if(mod == null) mod = buffer.get(newOid, newObj, true);
-                // if the object was deleted in an previous action, mark as new
-                // to avoid deletion, else mark object as dirty to assign the FK of
-                // the main object
-                if(mod.needsDelete())
-                {
-                    mod.setModificationState(mod.getModificationState().markNew());
-                }
-                else
-                {
-                    /*
-                    arminw: if the reference is a m:n relation and the object state is
-                    old clean, no need to update the reference.
-                    */
-                    if(!(cds.isMtoNRelation() && mod.getModificationState().equals(StateOldClean.getInstance())))
-                    {
-                        mod.setModificationState(mod.getModificationState().markDirty());
-                    }
-                }
-                // buffer this object as "new" in a list to prevent deletion
-                // when object was moved from one collection to another
-                buffer.addNewAssociatedIdentity(newOid);
-                // new referenced object found, so register all m:n relation for "linking"
-                if(cds.isMtoNRelation())
-                {
-                    buffer.addM2NLinkEntry(cds, getObject(), newObj);
-                }
-                else
-                {
-                    // we have to link the new object
-                    mod.addLinkOneToN(cds, getObject(), false);
-                }
-                if(mod.needsInsert())
-                {
-                    buffer.addForInsertDependent(mod);
-                }
+            Object key = entry.getKey();
+            // we only interested in references
+            if(key instanceof ObjectReferenceDescriptor)
+            {
+                Image oldRefImage = (Image) oldImage.get(key);
+                Image newRefImage = (Image) entry.getValue();
+                newRefImage.performReferenceDetection(oldRefImage);
             }
         }
     }
@@ -765,6 +650,8 @@
         if(log.isDebugEnabled()) log.debug("Start INSERT action for " + getIdentity());
         performLinkEntries();
         getBroker().store(getObject(), getIdentity(), getClassDescriptor(), true, true);
+        Identity oldOid = refreshIdentity();
+        buffer.replaceRegisteredIdentity(getIdentity(), oldOid);
     }
 
     public void doDelete()
@@ -779,26 +666,45 @@
         getBroker().removeFromCache(getIdentity());
     }
 
-    public boolean isWriteLocked()
+    boolean isWriteLocked()
     {
         return writeLocked;
     }
 
-    public void setWriteLocked(boolean writeLocked)
+    void setWriteLocked(boolean writeLocked)
     {
         this.writeLocked = writeLocked;
     }
 
+    boolean isMarkedForCascadingInsert()
+    {
+        return markedForCascadingInsert;
+    }
+
+    void markForCascadingInsert()
+    {
+        this.markedForCascadingInsert = true;
+    }
+
+    boolean isMarkedForCascadingDelete()
+    {
+        return markedForCascadingDelete;
+    }
+
+    void markForCascadingDelete()
+    {
+        this.markedForCascadingDelete = true;
+    }
+
     ClassDescriptor getClassDescriptor()
     {
-        return getBroker().getClassDescriptor(getBroker().getProxyFactory().getRealClass(getObject()));
+        return getBroker().getClassDescriptor(getProxyFactory().getRealClass(getObject()));
     }
 
     void addLinkOneToOne(ObjectReferenceDescriptor ord, boolean unlink)
     {
         LinkEntry entry = new LinkEntryOneToOne(ord, getObject(), unlink);
         linkEntryList.add(entry);
-        //setModificationState(getModificationState().markDirty());
     }
 
     void addLinkOneToN(CollectionDescriptor col, Object source, boolean unlink)
@@ -806,7 +712,6 @@
         if(col.isMtoNRelation()) throw new OJBRuntimeException("Expected an 1:n relation, but specified a m:n");
         LinkEntry entry = new LinkEntryOneToN(source, col, getObject(), unlink);
         linkEntryList.add(entry);
-        //setModificationState(getModificationState().markDirty());
     }
 
     private void performLinkEntries()
@@ -819,326 +724,132 @@
         }
     }
 
-
-    //====================================================
-    // inner class
-    //====================================================
-    /**
-     * Help to compare field values.
-     */
-    abstract class EqualsBase
-    {
-        /**
-         * This method is called before committing the transaction
-         * to allow cleanup of used resources, e.g. remove proxy listener objects
-         * to avoid invoke of registered objects after tx end.
-         */
-        abstract void cleanup(boolean reuse);
-    }
-
-    //====================================================
-    // inner class
-    //====================================================
-    /**
-     * Help to compare field values.
-     */
-    class EqualsFieldHelper extends EqualsBase
+    public void addedOneToOne(ObjectReferenceDescriptor ord, Object refObjOrProxy, Identity oid)
     {
-        FieldType type;
-        Object value;
-
-        public EqualsFieldHelper(FieldType type, Object value)
-        {
-            this.type = type;
-            this.value = value;
-        }
-
-        void cleanup(boolean reuse)
+        // the main objects needs link/unlink of the FK to 1:1 reference,
+        // so mark this dirty
+        setModificationState(getModificationState().markDirty());
+        // if the object is already registered, OJB knows about
+        // else lock and register object, get read lock, because we
+        // don't know if the object is new or moved from an existing other object
+        ObjectEnvelope oe = buffer.getByIdentity(oid);
+        if(oe == null)
         {
+            RuntimeObject rt = new RuntimeObject(refObjOrProxy, getTx());
+            // we don't use cascade locking, because new reference object
+            // will be detected by ObjectEnvelopeTable#cascadeMarkedForInsert()
+            getTx().lockAndRegister(rt, TransactionExt.READ, false);
         }
+        // in any case we need to link the main object
+        addLinkOneToOne(ord, false);
+    }
 
-        public boolean equals(Object valueNew)
+    public void deletedOneToOne(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(getModificationState().markDirty());
+        ObjectEnvelope oldRefMod = buffer.getByIdentity(oid);
+        // only delete when the reference wasn't assigned with another object
+        if(!buffer.isNewAssociatedObject(oid))
         {
-            boolean result = false;
-            if(this==valueNew)
+            // if cascading delete is enabled, remove the 1:1 reference
+            // because it was removed from the main object
+            if(buffer.getTransaction().cascadeDeleteFor(ord))
             {
-                result = true;
-            }
-            else
-            {
-                if(valueNew instanceof EqualsFieldHelper)
-                {
-                    result = type.equals(value, ((EqualsFieldHelper) valueNew).value);
-                }
+                oldRefMod.setModificationState(oldRefMod.getModificationState().markDelete());
             }
-//            if(!result)
-//            {
-//                System.out.println("** changed field: " + getIdentity() + ", this="+this + ", other=" + valueNew);
-//            }
-            return result;
-        }
-
-        public String toString()
-        {
-            return "EqualsFieldHelper[type=" + type + "->value=" + value + "]";
+            // unlink the main object
+            if(needsUnlink) addLinkOneToOne(ord, true);
         }
     }
 
-    //====================================================
-    // inner class
-    //====================================================
-    /**
-     * Help to compare 1:1 references of the main object.
-     */
-    class EqualsRefHelper extends EqualsBase
+    public void addedXToN(CollectionDescriptor cod, Object refObjOrProxy, Identity oid)
     {
-        Identity oid;
-        Object value;
-
-        public EqualsRefHelper(Object refObject)
-        {
-            this.value = refObject;
-        }
-
-        public EqualsRefHelper(Identity oid)
+        ObjectEnvelope mod = buffer.getByIdentity(oid);
+        // if the object isn't registered already, it can be 'new' or already 'persistent'
+        if(mod == null)
         {
-            this.oid = oid;
+            boolean isNew = getTx().isTransient(null, refObjOrProxy, oid);
+            mod = buffer.get(oid, refObjOrProxy, isNew);
         }
-
-        void cleanup(boolean reuse)
+        // if the object was deleted in an previous action, mark as new
+        // to avoid deletion, else mark object as dirty to assign the FK of
+        // the main object
+        if(mod.needsDelete())
         {
+            mod.setModificationState(mod.getModificationState().markNew());
         }
-
-        /**
-         * The reference is <em>null</em> when both Identity and Object attribute
-         * is null.
-         */
-        public boolean isNull()
-        {
-            return oid == null && value == null;
-        }
-
-        public boolean equals(Object toCompare)
+        else
         {
-            boolean result = false;
-            if(this==toCompare)
-            {
-                result = true;
-            }
-            else
+            /*
+            arminw: if the reference is a m:n relation and the object state is
+            old clean, no need to update the reference.
+            */
+            if(!(cod.isMtoNRelation() && mod.getModificationState().equals(StateOldClean.getInstance())))
             {
-                if(toCompare instanceof EqualsRefHelper)
-                {
-                    EqualsRefHelper other = (EqualsRefHelper) toCompare;
-                    if(oid == null)
-                    {
-                        if(value != null)
-                        {
-                            oid = getBroker().serviceIdentity().buildIdentity(value);
-                        }
-                    }
-                    if(other.oid == null)
-                    {
-                        if(other.value != null)
-                        {
-                            other.oid = getBroker().serviceIdentity().buildIdentity(other.value);
-                        }
-                    }
-                    // return oid != null ? oid.equals(other.oid) : other.oid == null;
-                    result = oid != null ? oid.equals(other.oid) : other.oid == null;
-                }
+                mod.setModificationState(mod.getModificationState().markDirty());
             }
-//            if(!result)
-//            {
-//                System.out.println("** changed 1:1: " + getIdentity() + ", ref: " + oid);
-//            }
-            return result;
-        }
-    }
-
-    //====================================================
-    // inner class
-    //====================================================
-    /**
-     * Help to compare 1:n / m:n references of the main object.
-     */
-    class EqualsColHelper extends EqualsBase implements CollectionProxyListener
-    {
-        private CollectionDescriptor des;
-        private CollectionProxy collectionHandler;
-        private int status;
-        private Map references;
-
-        public EqualsColHelper(CollectionDescriptor des, Object collOrArray)
-        {
-            this.des = des;
-            references = new HashMap();
-            assignReferenceObject(collOrArray);
-        }
-
-        public String toString()
-        {
-            return new ToStringBuilder(this)
-                    .append("main class", des.getClassDescriptor().getClassNameOfObject())
-                    .append("reference name", des.getPersistentField().getName())
-                    .append("reference class", des.getItemClassName())
-                    .append("status", status)
-                    .append("references", references)
-                    .toString();
         }
-
-        /**
-         * Always returns true, because changes in 1:n or m:n relations
-         * do not influence main object, no need for update main object
-         * thus return always true.
-         */
-        public boolean equals(Object obj)
+        // buffer this object as "new" in a list to prevent deletion
+        // when object was moved from one collection to another
+        buffer.addNewAssociatedIdentity(oid);
+        // new referenced object found, so register all m:n relation for "linking"
+        if(cod.isMtoNRelation())
         {
-            return (obj instanceof EqualsColHelper);
+            buffer.addM2NLinkEntry(cod, getObject(), refObjOrProxy);
         }
-
-        void cleanup(boolean reuse)
+        else
         {
-            if(!reuse && collectionHandler != null)
-            {
-                collectionHandler.removeListener(this);
-                collectionHandler = null;
-            }
+            // we have to link the new object
+            mod.addLinkOneToN(cod, getObject(), false);
         }
+// arminw: this object will be matched again in ObjectEnvelopeTable#cascadingDependents()
+// and then be added
+//        if(mod.needsInsert())
+//        {
+//            buffer.addForInsertDependent(mod);
+//        }
+    }
 
-        void assignReferenceObject(Object collOrArray)
+    public void deletedXToN(CollectionDescriptor cod, Object refObjOrProxy, Identity oid)
+    {
+        ObjectEnvelope mod = buffer.getByIdentity(oid);
+        // if this object is associated with another object it's
+        // not allowed to remove it, thus nothing will change
+        if(!buffer.isNewAssociatedObject(oid))
         {
-            CollectionProxy colProxy = getBroker().getProxyFactory().getCollectionProxy(collOrArray);
-            if(colProxy != null)
+            if(mod != null)
             {
-                if(colProxy.isLoaded())
-                {
-                    status = IS_MATERIALIZED_PROXY;
-                    /*
-                    TODO: how will Arrays be handled?
-                    */
-                    handleCollection(colProxy.iterator());
-                }
-                else
+                boolean cascade = buffer.getTransaction().cascadeDeleteFor(cod);
+                if(cascade)
                 {
-                    status = IS_UNMATERIALIZED_PROXY;
-// TODO: ObjectEnvelopeTable#register take care of proxy objects/collection, no need to handle proxy objects??
-                    colProxy.addListener(this);
-                    collectionHandler = colProxy;
+                    mod.setModificationState(mod.getModificationState().markDelete());
+                    // arminw: this object will be matched again in ObjectEnvelopeTable#cascadingDependents()
+                    // and then be added
+                    //buffer.addForDeletionDependent(mod);
                 }
-            }
-            else
-            {
-                status = IS_MATERIALIZED_OBJECT;
-                if(collOrArray != null)
+                if(cod.isMtoNRelation())
                 {
-                    Iterator it = BrokerHelper.getCollectionIterator(getBroker(), collOrArray);
-                    handleCollection(it);
+                    buffer.addM2NUnlinkEntry(cod, getObject(), refObjOrProxy);
                 }
-            }
-        }
-
-        public void beforeLoading(CollectionProxy colProxy)
-        {
-            //noop
-        }
-
-        public void afterLoading(CollectionProxy colProxy)
-        {
-            if(status == IS_UNMATERIALIZED_PROXY)
-            {
-                //System.out.println("**** materialize " + this + " ->> data: " + colProxy.getData());
-                // reference to the proxy data
-                handleMaterializedCollectionProxy(colProxy);
-                status = IS_MATERIALIZED_PROXY;
-                colProxy.removeListener(this);
-                collectionHandler = null;
-            }
-        }
-
-        void addReference(Identity oid, Object obj)
-        {
-            references.put(oid, obj);
-        }
-
-        void handleCollection(Iterator it)
-        {
-            if(it == null) return;
-            Object obj;
-            while(it.hasNext())
-            {
-                obj = it.next();
-                addReference(getBroker().serviceIdentity().buildIdentity(obj), obj);
-            }
-        }
-
-        void handleMaterializedCollectionProxy(CollectionProxy colProxy)
-        {
-            if(colProxy == null || colProxy.size() == 0)
-            {
-                // nothing to do
-                return;
-            }
-            // if the object materialize outside of a running tx (should not happen), we have to lookup
-            // a broker instance itself, so use PBCapsule to handle this
-            PBCapsule capsule = new PBCapsule(getTx().getBrokerInternal().getConfiguration(), getTx());
-            try
-            {
-                PersistenceBroker broker = capsule.getBroker();
-                Iterator it = colProxy.iterator();
-                Object tempObj;
-                IndirectionHandler tempHandler;
-                while(it.hasNext())
+                else
                 {
-                    tempObj = it.next();
-                    // the referenced objects can be proxy objects too
-                    tempHandler = getBroker().getProxyFactory().getIndirectionHandler(tempObj);
-                    if(tempHandler != null)
+                    // when 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)
                     {
-                        addReference(tempHandler.getIdentity(), tempObj);
-                    }
-                    else
-                    {
-                        addReference(broker.serviceIdentity().buildIdentity(tempObj), tempObj);
+                        mod.setModificationState(mod.getModificationState().markDirty());
+                        mod.addLinkOneToN(cod, getObject(), true);
                     }
                 }
             }
-            finally
+            else
             {
-                capsule.destroy();
+                throw new Image.ImageException("Unexpected behaviour, unregistered object to delete: "
+                        + oid + ", main object is " + getIdentity()+ ", envelope object is " + this.toString());
             }
         }
     }
-
-
-    //====================================================
-    // inner class
-    //====================================================
-    /**
-     * Thrown if something unexpected is happen when handling the
-     * object images for state detection.
-     */
-    public static class ImageExcetion extends OJBRuntimeException
-    {
-        public ImageExcetion()
-        {
-        }
-
-        public ImageExcetion(String msg)
-        {
-            super(msg);
-        }
-
-        public ImageExcetion(Throwable cause)
-        {
-            super(cause);
-        }
-
-        public ImageExcetion(String msg, Throwable cause)
-        {
-            super(msg, cause);
-        }
-    }
-
-
 }

Modified: db/ojb/trunk/src/java/org/apache/ojb/odmg/ObjectEnvelopeOrdering.java
URL: http://svn.apache.org/viewvc/db/ojb/trunk/src/java/org/apache/ojb/odmg/ObjectEnvelopeOrdering.java?rev=422235&r1=422234&r2=422235&view=diff
==============================================================================
--- db/ojb/trunk/src/java/org/apache/ojb/odmg/ObjectEnvelopeOrdering.java (original)
+++ db/ojb/trunk/src/java/org/apache/ojb/odmg/ObjectEnvelopeOrdering.java Sat Jul 15 07:28:03 2006
@@ -1,6 +1,6 @@
 package org.apache.ojb.odmg;
 
-/* Copyright 2002-2004 The Apache Software Foundation
+/* Copyright 2002-2005 The Apache Software Foundation
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -18,12 +18,12 @@
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
-import java.util.Map;
 
 import org.apache.commons.lang.ArrayUtils;
 import org.apache.commons.lang.builder.ToStringBuilder;
 import org.apache.ojb.broker.Identity;
 import org.apache.ojb.broker.PersistenceBrokerInternal;
+import org.apache.ojb.broker.core.proxy.ProxyFactory;
 import org.apache.ojb.broker.metadata.ClassDescriptor;
 import org.apache.ojb.broker.metadata.CollectionDescriptor;
 import org.apache.ojb.broker.metadata.ObjectReferenceDescriptor;
@@ -86,28 +86,20 @@
     private static Logger log = LoggerFactory.getLogger(ObjectEnvelopeOrdering.class);
 
     private List originalOrder;
-    private Map envelopes;
 
     private Vertex[] vertices;
     private List edgeList;
 
-    private int newOrderIndex;
-    private Identity[] newOrder;
-
-    private PersistenceBrokerInternal broker;
+    private ObjectEnvelope[] newOrder;
 
     /**
      * Creates an object envelope ordering based on an original ordering
      * of Identity objects and an Identity-&gt;ObjectEnvelope map
      * @param originalOrder a list of Identity objects
-     * @param envelopes a map with ObjectEnvelope-s with their respective
-     *      Identity-s as key
      */
-    public ObjectEnvelopeOrdering(PersistenceBrokerInternal broker, List originalOrder, Map envelopes)
+    public ObjectEnvelopeOrdering(List originalOrder)
     {
         this.originalOrder = originalOrder;
-        this.envelopes = envelopes;
-        this.broker = broker;
     }
 
     /**
@@ -117,13 +109,14 @@
      */
     public void reorder()
     {
-        long t1 = 0, t2 = 0, t3 = 0;
+        int newOrderIndex = 0;
+        long t1 = 0, t2 = 0, t3;
+
         if (log.isDebugEnabled())
         {
             t1 = System.currentTimeMillis();
         }
-        newOrder = new Identity[originalOrder.size()];
-        newOrderIndex = 0;
+        newOrder = new ObjectEnvelope[originalOrder.size()];
 
         if(log.isDebugEnabled()) log.debug("Orginal order: " + originalOrder);
         // set up the vertex array in the order the envelopes were added
@@ -131,7 +124,7 @@
         // int vertexIndex = 0;
         for (Iterator it = originalOrder.iterator(); it.hasNext();)
         {
-            ObjectEnvelope envelope = (ObjectEnvelope) envelopes.get(it.next());
+            ObjectEnvelope envelope = (ObjectEnvelope) it.next();
             if (envelope.needsUpdate() || envelope.needsInsert() || envelope.needsDelete())
             {
                 Vertex vertex = new Vertex(envelope);
@@ -144,7 +137,7 @@
             else
             {
                 // envelope is clean - just add identity to new order
-                newOrder[newOrderIndex++] = envelope.getIdentity();
+                newOrder[newOrderIndex++] = envelope;
                 if (log.isDebugEnabled())
                 {
                     log.debug("Add unmodified object "+envelope.getIdentity()+" to new OrderList");
@@ -213,7 +206,7 @@
                 Vertex vertex = vertices[i];
                 if (!vertex.isProcessed() && vertex.getIncomingEdgeWeight() == minIncomingEdgeWeight)
                 {
-                    newOrder[newOrderIndex++] = vertex.getEnvelope().getIdentity();
+                    newOrder[newOrderIndex++] = vertex.getEnvelope();
                     vertex.markProcessed();
                     processCount++;
                     if (log.isDebugEnabled())
@@ -247,7 +240,7 @@
      * @return an array of Identity objects representing the opimized sequence
      *      of database operations
      */
-    public Identity[] getOrdering()
+    public ObjectEnvelope[] getOrdering()
     {
         if (newOrder == null)
         {
@@ -286,7 +279,7 @@
      */
     private void addObjectReferenceEdges(Vertex vertex, ObjectReferenceDescriptor rds)
     {
-        Object refObject = rds.getPersistentField().get(vertex.getEnvelope().getObject());
+        Object refObject = rds.getPersistentField().get(vertex.getEnvelope().getRealObject());
         Class refClass = rds.getItemClass();
         for (int i = 0; i < vertices.length; i++)
         {
@@ -294,13 +287,13 @@
             // ObjectEnvelope envelope = vertex.getEnvelope();
             Vertex refVertex = vertices[i];
             ObjectEnvelope refEnvelope = refVertex.getEnvelope();
-            if (refObject == refEnvelope.getObject())
+            if (refObject == refEnvelope.getRealObject())
             {
-                edge = buildConcrete11Edge(vertex, refVertex, rds.hasForeignKey());
+                edge = buildConcrete11Edge(vertex, refVertex, rds.hasConstraint());
             }
-            else if (refClass.isInstance(refVertex.getEnvelope().getObject()))
+            else if (refClass.isInstance(refVertex.getEnvelope().getRealObject()))
             {
-                edge = buildPotential11Edge(vertex, refVertex, rds.hasForeignKey());
+                edge = buildPotential11Edge(vertex, refVertex, rds.hasConstraint());
             }
             if (edge != null)
             {
@@ -325,16 +318,16 @@
     private void addCollectionEdges(Vertex vertex, CollectionDescriptor cds)
     {
         ObjectEnvelope envelope = vertex.getEnvelope();
-        Object col = cds.getPersistentField().get(envelope.getObject());
+        Object col = cds.getPersistentField().get(envelope.getRealObject());
         Object[] refObjects;
-        if (col == null || (broker.getProxyFactory().isCollectionProxy(col)
-                && !broker.getProxyFactory().getCollectionProxy(col).isLoaded()))
+        ProxyFactory pf = envelope.getBroker().getProxyFactory();
+        if (col == null || (pf.isCollectionProxy(col) && !pf.getCollectionProxy(col).isLoaded()))
         {
             refObjects = EMPTY_OBJECT_ARRAY;
         }
         else
         {
-            refObjects = BrokerHelper.getCollectionArray(broker, col);
+            refObjects = BrokerHelper.getCollectionArray(envelope.getBroker(), col);
         }
         Class refClass = cds.getItemClass();
 
@@ -344,9 +337,9 @@
             Vertex refVertex = vertices[i];
             ObjectEnvelope refEnvelope = refVertex.getEnvelope();
 
-            if (refClass.isInstance(refEnvelope.getObject()))
+            if (refClass.isInstance(refEnvelope.getRealObject()))
             {
-                if (containsObject(refEnvelope.getObject(), refObjects))
+                if (containsObject(refEnvelope.getRealObject(), refObjects))
                 {
                     if (cds.isMtoNRelation())
                     {



---------------------------------------------------------------------
To unsubscribe, e-mail: ojb-dev-unsubscribe@db.apache.org
For additional commands, e-mail: ojb-dev-help@db.apache.org