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]