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]