Author: arminw
Date: Tue May 15 16:13:16 2007
New Revision: 538374

URL: http://svn.apache.org/viewvc?view=rev&rev=538374
Log:
optimze m:n indirection table handling, move m:n specificcontent from PB to 
this class

Modified:
    
db/ojb/branches/OJB_1_0_RELEASE/src/java/org/apache/ojb/broker/core/MtoNBroker.java

Modified: 
db/ojb/branches/OJB_1_0_RELEASE/src/java/org/apache/ojb/broker/core/MtoNBroker.java
URL: 
http://svn.apache.org/viewvc/db/ojb/branches/OJB_1_0_RELEASE/src/java/org/apache/ojb/broker/core/MtoNBroker.java?view=diff&rev=538374&r1=538373&r2=538374
==============================================================================
--- 
db/ojb/branches/OJB_1_0_RELEASE/src/java/org/apache/ojb/broker/core/MtoNBroker.java
 (original)
+++ 
db/ojb/branches/OJB_1_0_RELEASE/src/java/org/apache/ojb/broker/core/MtoNBroker.java
 Tue May 15 16:13:16 2007
@@ -20,10 +20,11 @@
 import java.util.Collection;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Collections;
+import java.util.Set;
+import java.util.HashSet;
 
 import org.apache.commons.lang.ArrayUtils;
-import org.apache.commons.lang.builder.EqualsBuilder;
-import org.apache.commons.lang.builder.HashCodeBuilder;
 import org.apache.commons.lang.builder.ToStringBuilder;
 import org.apache.ojb.broker.MtoNImplementor;
 import org.apache.ojb.broker.OJBRuntimeException;
@@ -51,11 +52,11 @@
 
     private PersistenceBrokerImpl pb;
     /**
-     * Used to store [EMAIL PROTECTED] GenericObject} while transaction 
running, used as
+     * Used to store [EMAIL PROTECTED] MtoNEntry} while transaction running, 
used as
      * workaround for m:n insert problem.
      * TODO: find better solution for m:n handling
      */
-    private List tempObjects = new ArrayList();
+    private Set insertedMtoNEntries = new HashSet();
 
     public MtoNBroker(final PersistenceBrokerImpl broker)
     {
@@ -64,7 +65,7 @@
 
     public void reset()
     {
-        tempObjects.clear();
+        insertedMtoNEntries.clear();
     }
 
     /**
@@ -73,65 +74,69 @@
      * @param cod        The [EMAIL PROTECTED] 
org.apache.ojb.broker.metadata.CollectionDescriptor} for the m:n relation
      * @param realObject The real object
      * @param otherObj   The referenced object
-     * @param mnKeys     The all [EMAIL PROTECTED] 
org.apache.ojb.broker.core.MtoNBroker.Key} matching the real object
+     * @param existingFk The all [EMAIL PROTECTED] Key foreign keys} matching 
the real object.
      */
-    public void storeMtoNImplementor(CollectionDescriptor cod, Object 
realObject, Object otherObj, Collection mnKeys)
+    public void storeMtoNImplementor(CollectionDescriptor cod, Object 
realObject, Object otherObj,
+                                     Collection existingFk)
     {
         ClassDescriptor cld = 
pb.getDescriptorRepository().getDescriptorFor(realObject.getClass());
-        ValueContainer[] pkValues = pb.serviceBrokerHelper().getKeyValues(cld, 
realObject);
-        String[] pkColumns = cod.getFksToThisClass();
+        storeMtoNImplementor(realObject, cld, cod, otherObj, existingFk);
+    }
 
-        ClassDescriptor otherCld = 
pb.getDescriptorRepository().getDescriptorFor(pb.getProxyFactory().getRealClass(otherObj));
+    protected void storeMtoNImplementor(Object realObject, ClassDescriptor 
cld, CollectionDescriptor cod,
+                                        Object otherObj, Collection existingFk)
+    {
+        ClassDescriptor otherCld = cod.getItemClassDescriptor();
         ValueContainer[] otherPkValues = 
pb.serviceBrokerHelper().getKeyValues(otherCld, otherObj);
-
-        String[] otherPkColumns = cod.getFksToItemClass();
-        String table = cod.getIndirectionTable();
-        MtoNBroker.Key key = new MtoNBroker.Key(otherPkValues);
-
-        if(mnKeys.contains(key))
+        Key key = new Key(otherPkValues);
+        if(existingFk.contains(key))
         {
             return;
         }
 
+        String[] otherPkColumns = cod.getFksToItemClass();
+        String[] pkColumns = cod.getFksToThisClass();
+        ValueContainer[] pkValues = pb.serviceBrokerHelper().getKeyValues(cld, 
realObject);
         /*
         fix for OJB-76, composite M & N keys that have some fields common
         find the "shared" indirection table columns, values and remove these 
from m- or n- side
         */
-        for(int i = 0; i < otherPkColumns.length; i++)
+        int[] shared = findSharedColumns(pkColumns, otherPkColumns);
+        if(shared.length > 0)
         {
-            int index = ArrayUtils.indexOf(pkColumns, otherPkColumns[i]);
-            if(index != -1)
-            {
-                // shared indirection table column found, remove this column 
from one side
-                pkColumns = (String[]) ArrayUtils.remove(pkColumns, index);
-                // remove duplicate value too
-                pkValues = (ValueContainer[]) ArrayUtils.remove(pkValues, 
index);
-            }
+            pkColumns = removeSharedColumns(pkColumns, shared);
+            pkValues = removeSharedValues(pkValues, shared);
         }
 
         String[] cols = mergeColumns(pkColumns, otherPkColumns);
+        String table = cod.getIndirectionTable();
         String insertStmt = 
pb.serviceSqlGenerator().getInsertMNStatement(table, pkColumns, otherPkColumns);
+
         ValueContainer[] values = mergeContainer(pkValues, otherPkValues);
-        GenericObject gObj = new GenericObject(table, cols, values);
-        if(! tempObjects.contains(gObj))
+        MtoNEntry gObj = new MtoNEntry(table, cols, values);
+        if(!insertedMtoNEntries.contains(gObj))
         {
             pb.serviceJdbcAccess().executeUpdateSQL(insertStmt, cld, pkValues, 
otherPkValues);
-            tempObjects.add(gObj);
+            insertedMtoNEntries.add(gObj);
         }
     }
 
     /**
-     * get a Collection of Keys of already existing m:n rows
+     * Get a list of already existing [EMAIL PROTECTED] 
org.apache.ojb.broker.core.MtoNBroker.Key foreign keys}
+     * (matching the other side of the m:n reference in the indirection table) 
of the specified object.
      *
-     * @param cod
-     * @param obj
-     * @return Collection of Key
+     * @param cod The [EMAIL PROTECTED] 
org.apache.ojb.broker.metadata.ClassDescriptor} of the object.
+     * @param obj The real none proxy object.
+     * @return List of [EMAIL PROTECTED] Key} objects.
      */
     public List getMtoNImplementor(CollectionDescriptor cod, Object obj)
     {
-        ResultSetAndStatement rs = null;
-        ArrayList result = new ArrayList();
         ClassDescriptor cld = 
pb.getDescriptorRepository().getDescriptorFor(obj.getClass());
+        return getMtoNImplementor(obj, cld, cod);
+    }
+
+    protected List getMtoNImplementor(Object obj, ClassDescriptor cld, 
CollectionDescriptor cod)
+    {
         ValueContainer[] pkValues = pb.serviceBrokerHelper().getKeyValues(cld, 
obj);
         String[] pkColumns = cod.getFksToThisClass();
         String[] fkColumns = cod.getFksToItemClass();
@@ -140,11 +145,6 @@
         String selectStmt = 
pb.serviceSqlGenerator().getSelectMNStatement(table, fkColumns, pkColumns);
 
         ClassDescriptor itemCLD = cod.getItemClassDescriptor();
-        Collection extents = 
pb.getDescriptorRepository().getAllConcreteSubclassDescriptors(itemCLD);
-        if(extents.size() > 0)
-        {
-            itemCLD = (ClassDescriptor) extents.iterator().next();
-        }
         FieldDescriptor[] itemClassPKFields = itemCLD.getPkFields();
         if(itemClassPKFields.length != fkColumns.length)
         {
@@ -154,6 +154,8 @@
                     " Declared 'fk-pointing-to-element-class' elements in 
collection-descriptor are"
                     + fkColumns.length);
         }
+        ResultSetAndStatement rs = null;
+        ArrayList result = new ArrayList();
         try
         {
             rs = pb.serviceJdbcAccess().executeSQL(selectStmt, cld, pkValues, 
Query.NOT_SCROLLABLE);
@@ -164,7 +166,7 @@
                 {
                     row[i] = new ValueContainer(rs.m_rs.getObject(i + 1), 
itemClassPKFields[i].getJdbcType());
                 }
-                result.add(new MtoNBroker.Key(row));
+                result.add(new Key(row));
             }
         }
         catch(PersistenceBrokerException e)
@@ -183,10 +185,11 @@
     }
 
     /**
-     * delete all rows from m:n table belonging to obj
+     * Delete all rows from the m:n indirection table belonging to the
+     * specified object.
      *
-     * @param cod
-     * @param obj
+     * @param cod The m:n [EMAIL PROTECTED] 
org.apache.ojb.broker.metadata.CollectionDescriptor}.
+     * @param obj The object.
      */
     public void deleteMtoNImplementor(CollectionDescriptor cod, Object obj)
     {
@@ -199,49 +202,100 @@
     }
 
     /**
-     * deletes all rows from m:n table that are not used in relatedObjects
+     * Delete all rows from the m:n indirection table belonging to the
+     * specified object and can't be found in the existing FK list of the 
referenced objects.
      *
      * @param cod
      * @param obj
-     * @param collectionIterator
-     * @param mnKeys
+     * @param referencedObjects
+     * @param existingFk All related [EMAIL PROTECTED] Key foreign key} of the 
referenced objects in the indirection table.
      */
-    public void deleteMtoNImplementor(CollectionDescriptor cod, Object obj, 
Iterator collectionIterator, Collection mnKeys)
+    public void deleteMtoNImplementor(CollectionDescriptor cod, Object obj, 
Iterator referencedObjects,
+                                      Collection existingFk)
+    {
+        if(existingFk.isEmpty() || referencedObjects == null)
+        {
+            return;
+        }
+        ClassDescriptor cld = 
pb.getDescriptorRepository().getDescriptorFor(obj.getClass());
+        deleteMtoNImplementor(cld, obj, cod, referencedObjects, existingFk);
+    }
+
+    protected void deleteMtoNImplementor(ClassDescriptor cld, Object obj, 
CollectionDescriptor cod,
+                                         Iterator referencedObjects, 
Collection existingFkEntries)
     {
-        if(mnKeys.isEmpty() || collectionIterator == null)
+        if(existingFkEntries.isEmpty() || referencedObjects == null)
         {
             return;
         }
-        List workList = new ArrayList(mnKeys);
-        MtoNBroker.Key relatedObjKeys;
+        List workList = new ArrayList(existingFkEntries);
         ClassDescriptor relatedCld = cod.getItemClassDescriptor();
 
         BrokerHelper helper = pb.serviceBrokerHelper();
         Object relatedObj;
+        Key relatedEntry;
         // remove keys of relatedObject from the existing m:n rows in workList
-        while(collectionIterator.hasNext())
+        while(referencedObjects.hasNext())
         {
-            relatedObj = collectionIterator.next();
-            relatedObjKeys = new 
MtoNBroker.Key(helper.getKeyValues(relatedCld, relatedObj, true));
-            workList.remove(relatedObjKeys);
+            relatedObj = referencedObjects.next();
+            ValueContainer[] fkValues = helper.getKeyValues(relatedCld, 
relatedObj, true);
+            relatedEntry = new Key(fkValues);
+            workList.remove(relatedEntry);
+        }
+
+        if(!workList.isEmpty())
+        {
+            String table = cod.getIndirectionTable();
+            ValueContainer[] pkValues = helper.getKeyValues(cld, obj);
+            String[] pkColumns = cod.getFksToThisClass();
+            String[] fkColumns = cod.getFksToItemClass();
+            String deleteStmt = null;
+            // delete all remaining indirection table entries
+            for(int i = 0; i < workList.size(); i++)
+            {
+                Key key = (Key) workList.get(i);
+                if(deleteStmt == null)
+                {
+                    deleteStmt = 
pb.serviceSqlGenerator().getDeleteMNStatement(table, pkColumns, fkColumns);
+                }
+                pb.serviceJdbcAccess().executeUpdateSQL(deleteStmt, cld, 
pkValues, key.containers);
+            }
         }
+    }
 
-        // delete all remaining keys in workList
+    /**
+     * Store all new indirection table entries of the specified object and its 
referenced objects.
+     * @param obj The main non-proxy object.
+     * @param cod The [EMAIL PROTECTED] 
org.apache.ojb.broker.metadata.CollectionDescriptor} of the main object.
+     * @param referencedObjects The referenced objects.
+     * @param insert If <em>true</em> the main object will be inserted, else 
<em>false</em>.
+     */
+    public void storeMtoN(Object obj, CollectionDescriptor cod, Object 
referencedObjects, boolean insert)
+    {
         ClassDescriptor cld = 
pb.getDescriptorRepository().getDescriptorFor(obj.getClass());
-        ValueContainer[] pkValues = helper.getKeyValues(cld, obj);
-
-        String[] pkColumns = cod.getFksToThisClass();
-        String[] fkColumns = cod.getFksToItemClass();
-        String table = cod.getIndirectionTable();
-        String deleteStmt;
-
-        ValueContainer[] fkValues;
-        Iterator iter = workList.iterator();
-        while(iter.hasNext())
-        {
-            fkValues = ((MtoNBroker.Key) iter.next()).m_containers;
-            deleteStmt = pb.serviceSqlGenerator().getDeleteMNStatement(table, 
pkColumns, fkColumns);
-            pb.serviceJdbcAccess().executeUpdateSQL(deleteStmt, cld, pkValues, 
fkValues);
+        Iterator referencedObjectsIterator;
+        Collection existingMtoNKeys;
+        if(!insert)
+        {
+            existingMtoNKeys = getMtoNImplementor(obj, cld, cod);
+            // we can't reuse iterator
+            referencedObjectsIterator = 
BrokerHelper.getCollectionIterator(referencedObjects);
+            // remove all entries in indirection table which not be part of 
referenced objects
+            deleteMtoNImplementor(cld, obj, cod, referencedObjectsIterator, 
existingMtoNKeys);
+        }
+        else
+        {
+            existingMtoNKeys = Collections.EMPTY_LIST;
+        }
+        // we can't reuse iterator
+        referencedObjectsIterator = 
BrokerHelper.getCollectionIterator(referencedObjects);
+        while (referencedObjectsIterator.hasNext())
+        {
+            Object refObj = referencedObjectsIterator.next();
+            // Now store indirection record
+            // BRJ: this could cause integrity problems because
+            // obj may not be stored depending on auto-update
+            storeMtoNImplementor(obj, cld, cod, refObj, existingMtoNKeys);
         }
     }
 
@@ -308,11 +362,11 @@
             if(insert)
             {
                 stmt = pb.serviceSqlGenerator().getInsertMNStatement(table, 
pkLeftColumns, pkRightColumns);
-                GenericObject gObj = new GenericObject(table, cols, values);
-                if(!tempObjects.contains(gObj))
+                MtoNEntry gObj = new MtoNEntry(table, cols, values);
+                if(!insertedMtoNEntries.contains(gObj))
                 {
                     pb.serviceJdbcAccess().executeUpdateSQL(stmt, leftCld, 
leftPkValues, rightPkValues);
-                    tempObjects.add(gObj);
+                    insertedMtoNEntries.add(gObj);
                 }
             }
             else
@@ -339,22 +393,72 @@
         return values;
     }
 
+    private String[] removeSharedColumns(String[] columns, int[] indexToRemove)
+    {
+        return (String[]) removeShared(columns, indexToRemove);
+    }
 
+    private ValueContainer[] removeSharedValues(ValueContainer[] values, int[] 
indexToRemove)
+    {
+        return (ValueContainer[]) removeShared(values, indexToRemove);
+    }
+
+    /**
+     * Expects the shared indices in ascending order.
+     */
+    private Object[] removeShared(Object[] objects, int[] indexToRemove)
+    {
+        Object[] result = objects;
+        for(int i = indexToRemove.length - 1; i > -1; i--)
+        {
+            int k = indexToRemove[i];
+            result = ArrayUtils.remove(objects, k);
+        }
+        return result;
+    }
+
+    /**
+     * Return shared columns index in ascending order.
+     */
+    private int[] findSharedColumns(String[] pkColumns, String[] fkColumns)
+    {
+        int[] result = {};
+        if(pkColumns.length > 1 || fkColumns.length > 1)
+        {
+            /*
+            fix for OJB-76, composite M & N keys that have some fields common
+            find the "shared" indirection table columns, values and remove 
these from m- or n- side
+            */
+            int startIndex = 0;
+            for(int i = 0; i < fkColumns.length; i++)
+            {
+                int index = ArrayUtils.indexOf(pkColumns, fkColumns[i], 
startIndex);
+                if(index != -1)
+                {
+                    result = ArrayUtils.add(result, index);
+                    startIndex = index;
+                }
+            }
+        }
+        return result;
+    }
 
 // ************************************************************************
-// inner class
+// inner classes
 // ************************************************************************
 
     /**
-     * This is a helper class to model a Key of an Object
+     * This is a helper class to model a Key of an Object, e.g. the
+     * foreign key of a referenced m:n object in the indirection table.
      */
     private static final class Key
     {
-        final ValueContainer[] m_containers;
+        final ValueContainer[] containers;
+        int hashCode;
 
         Key(final ValueContainer[] containers)
         {
-            m_containers = new ValueContainer[containers.length];
+            this.containers = new ValueContainer[containers.length];
 
             for(int i = 0; i < containers.length; i++)
             {
@@ -372,7 +476,7 @@
                     value = new Long(((Number) value).longValue());
                 }
 
-                m_containers[i] = new ValueContainer(value, type);
+                this.containers[i] = new ValueContainer(value, type);
             }
         }
 
@@ -386,35 +490,45 @@
             {
                 return false;
             }
-
+            boolean result = true;
             Key otherKey = (Key) other;
-            EqualsBuilder eb = new EqualsBuilder();
-
-            eb.append(m_containers, otherKey.m_containers);
-            return eb.isEquals();
+            for(int i = 0; i < containers.length; i++)
+            {
+                ValueContainer container = containers[i];
+                if(!container.equals(otherKey.containers[i]))
+                {
+                    result = false;
+                    break;
+                }
+            }
+            return result;
         }
 
         public int hashCode()
         {
-            HashCodeBuilder hb = new HashCodeBuilder();
-            hb.append(m_containers);
-
-            return hb.toHashCode();
+            if(hashCode == 0)
+            {
+                for(int i = 0; i < containers.length; i++)
+                {
+                    ValueContainer container = containers[i];
+                    hashCode += container.hashCode();
+                }
+            }
+            return hashCode;
         }
     }
 
-
-
-    // ************************************************************************
-    // inner class
-    // ************************************************************************
-    private static final class GenericObject
+    /**
+     * This class represents an indirection table row.
+     */
+    private static final class MtoNEntry
     {
-         private String tablename;
+        private String tablename;
         private String[] columnNames;
         private ValueContainer[] values;
+        private int hashCode;
 
-        public GenericObject(String tablename, String[] columnNames, 
ValueContainer[] values)
+        public MtoNEntry(String tablename, String[] columnNames, 
ValueContainer[] values)
         {
             this.tablename = tablename;
             this.columnNames = columnNames;
@@ -432,10 +546,10 @@
                 return true;
             }
             boolean result = false;
-            if(obj instanceof GenericObject)
+            if(obj instanceof MtoNEntry)
             {
-                GenericObject other = (GenericObject) obj;
-                result = (tablename.equalsIgnoreCase(other.tablename)
+                MtoNEntry other = (MtoNEntry) obj;
+                result = (tablename.equals(other.tablename)
                         && (columnNames != null)
                         && (other.columnNames != null)
                         && (columnNames.length == other.columnNames.length));
@@ -474,7 +588,16 @@
 
         public int hashCode()
         {
-            return super.hashCode();
+            if(hashCode == 0)
+            {
+                for(int i = 0; i < values.length; i++)
+                {
+                     hashCode += values[i].hashCode();
+                     //hashCode += columnNames[i].hashCode();
+                }
+                //hashCode = tablename.hashCode() + hashCode;
+            }
+            return hashCode;
         }
 
         public ValueContainer getValueFor(String columnName)



---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to