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 ol...@apache.org on 2003/07/24 23:55:04 UTC
cvs commit: db-ojb/src/test/org/apache/ojb/otm Address.java AddressDesc.java DependentTests.java Person.java AllTests.java
olegnitz 2003/07/24 14:55:04
Modified: src/java/org/apache/ojb/broker/metadata
CollectionDescriptor.java
ObjectReferenceDescriptor.java
RepositoryElements.java RepositoryTags.java
RepositoryXmlHandler.java
src/java/org/apache/ojb/otm/core ConcreteEditingContext.java
src/schema ojbtest-schema.xml
src/test/org/apache/ojb repository.dtd repository_junit.xml
src/test/org/apache/ojb/otm AllTests.java
Added: src/test/org/apache/ojb/otm Address.java AddressDesc.java
DependentTests.java Person.java
Log:
Added the notion of dependent objects to OTM and the test for it.
Added the "otm-dependent" attribute for collection and reference descriptors
in repository.xml
Revision Changes Path
1.20 +7 -1 db-ojb/src/java/org/apache/ojb/broker/metadata/CollectionDescriptor.java
Index: CollectionDescriptor.java
===================================================================
RCS file: /home/cvs/db-ojb/src/java/org/apache/ojb/broker/metadata/CollectionDescriptor.java,v
retrieving revision 1.19
retrieving revision 1.20
diff -u -r1.19 -r1.20
--- CollectionDescriptor.java 2 May 2003 12:24:31 -0000 1.19
+++ CollectionDescriptor.java 24 Jul 2003 21:55:03 -0000 1.20
@@ -330,6 +330,12 @@
result += " " + tags.getAttribute(AUTO_DELETE,"true") + eol;
}
+ //otm-dependent is optional, disabled by default
+ if (getOtmDependent())
+ {
+ result += " " + tags.getAttribute(OTM_DEPENDENT, "true") + eol;
+ }
+
// close opening tag
result += " >" + eol;
1.29 +25 -2 db-ojb/src/java/org/apache/ojb/broker/metadata/ObjectReferenceDescriptor.java
Index: ObjectReferenceDescriptor.java
===================================================================
RCS file: /home/cvs/db-ojb/src/java/org/apache/ojb/broker/metadata/ObjectReferenceDescriptor.java,v
retrieving revision 1.28
retrieving revision 1.29
diff -u -r1.28 -r1.29
--- ObjectReferenceDescriptor.java 13 Jun 2003 18:48:12 -0000 1.28
+++ ObjectReferenceDescriptor.java 24 Jul 2003 21:55:03 -0000 1.29
@@ -81,7 +81,8 @@
private boolean m_CascadeDelete = false;
private Class m_ProxyOfItems = null;
private boolean m_LookedUpProxy = false;
-
+ private boolean m_OtmDependent = false;
+
/**
* holds the foreign-key field descriptor array for a specified class
*/
@@ -343,6 +344,22 @@
m_CascadeDelete = b;
}
+ /**
+ *
+ */
+ public boolean getOtmDependent()
+ {
+ return m_OtmDependent;
+ }
+
+ /**
+ *
+ */
+ public void setOtmDependent(boolean b)
+ {
+ m_OtmDependent = b;
+ }
+
public String toString()
{
return new ToStringBuilder(this)
@@ -407,6 +424,12 @@
result += " " + tags.getAttribute(AUTO_DELETE, "true") + eol;
}
+ //otm-dependent is optional, disabled by default
+ if (getOtmDependent())
+ {
+ result += " " + tags.getAttribute(OTM_DEPENDENT, "true") + eol;
+ }
+
// close opening tag
result += " >" + eol;
@@ -415,7 +438,7 @@
for (int i = 0; i < getForeignKeyFields().size(); i++)
{
Object obj = getForeignKeyFields().get(i);
- if (obj instanceof Integer)
+ if (obj instanceof Integer)
{
String fkId = obj.toString();
result += " " + tags.getOpeningTagNonClosingById(FOREIGN_KEY) + " ";
1.30 +2 -1 db-ojb/src/java/org/apache/ojb/broker/metadata/RepositoryElements.java
Index: RepositoryElements.java
===================================================================
RCS file: /home/cvs/db-ojb/src/java/org/apache/ojb/broker/metadata/RepositoryElements.java,v
retrieving revision 1.29
retrieving revision 1.30
diff -u -r1.29 -r1.30
--- RepositoryElements.java 23 Jul 2003 20:07:32 -0000 1.29
+++ RepositoryElements.java 24 Jul 2003 21:55:03 -0000 1.30
@@ -102,6 +102,7 @@
public static final int AUTO_RETRIEVE = 24;
public static final int AUTO_UPDATE = 25;
public static final int AUTO_DELETE = 26;
+ public static final int OTM_DEPENDENT = 102;
public static final int COLLECTION_DESCRIPTOR = 27;
public static final int ITEMS_CLASS = 29;
public static final int INVERSE_FK = 38;
@@ -165,7 +166,7 @@
public static final int NAME = 97;
// maintain a next id to keep track where we are
- static final int _NEXT = 102;
+ static final int _NEXT = 103;
// String constants
public static final String TAG_ACCESS = "access";
1.30 +1 -0 db-ojb/src/java/org/apache/ojb/broker/metadata/RepositoryTags.java
Index: RepositoryTags.java
===================================================================
RCS file: /home/cvs/db-ojb/src/java/org/apache/ojb/broker/metadata/RepositoryTags.java,v
retrieving revision 1.29
retrieving revision 1.30
diff -u -r1.29 -r1.30
--- RepositoryTags.java 23 Jul 2003 20:07:32 -0000 1.29
+++ RepositoryTags.java 24 Jul 2003 21:55:03 -0000 1.30
@@ -155,6 +155,7 @@
table.put("refresh", new Integer(REFRESH));
table.put("proxy", new Integer(PROXY_REFERENCE));
table.put("sort", new Integer(SORT));
+ table.put("otm-dependent", new Integer(OTM_DEPENDENT));
table.put("index-descriptor", new Integer(INDEX_DESCRIPTOR));
table.put("index-column", new Integer(INDEX_COLUMN));
1.45 +13 -1 db-ojb/src/java/org/apache/ojb/broker/metadata/RepositoryXmlHandler.java
Index: RepositoryXmlHandler.java
===================================================================
RCS file: /home/cvs/db-ojb/src/java/org/apache/ojb/broker/metadata/RepositoryXmlHandler.java,v
retrieving revision 1.44
retrieving revision 1.45
diff -u -r1.44 -r1.45
--- RepositoryXmlHandler.java 23 Jul 2003 20:07:32 -0000 1.44
+++ RepositoryXmlHandler.java 24 Jul 2003 21:55:03 -0000 1.45
@@ -521,6 +521,12 @@
b = (new Boolean(autoDelete)).booleanValue();
m_CurrentORD.setCascadeDelete(b);
+ //set otm-dependent attribute
+ String otmDependent = atts.getValue(tags.getTagById(OTM_DEPENDENT));
+ if (isDebug) logger.debug(" " + tags.getTagById(OTM_DEPENDENT) + ": " + otmDependent);
+ b = (new Boolean(otmDependent)).booleanValue();
+ m_CurrentORD.setOtmDependent(b);
+
break;
}
@@ -627,6 +633,12 @@
if (isDebug) logger.debug(" " + tags.getTagById(AUTO_DELETE) + ": " + autoDelete);
b = (new Boolean(autoDelete)).booleanValue();
m_CurrentCOD.setCascadeDelete(b);
+
+ //set otm-dependent attribute
+ String otmDependent = atts.getValue(tags.getTagById(OTM_DEPENDENT));
+ if (isDebug) logger.debug(" " + tags.getTagById(OTM_DEPENDENT) + ": " + otmDependent);
+ b = (new Boolean(otmDependent)).booleanValue();
+ m_CurrentCOD.setOtmDependent(b);
m_CurrentCLD.addCollectionDescriptor(m_CurrentCOD);
1.21 +389 -87 db-ojb/src/java/org/apache/ojb/otm/core/ConcreteEditingContext.java
Index: ConcreteEditingContext.java
===================================================================
RCS file: /home/cvs/db-ojb/src/java/org/apache/ojb/otm/core/ConcreteEditingContext.java,v
retrieving revision 1.20
retrieving revision 1.21
diff -u -r1.20 -r1.21
--- ConcreteEditingContext.java 13 Jul 2003 14:07:16 -0000 1.20
+++ ConcreteEditingContext.java 24 Jul 2003 21:55:03 -0000 1.21
@@ -75,7 +75,6 @@
import java.util.Set;
import org.apache.ojb.broker.Identity;
-import org.apache.ojb.broker.ManageableCollection;
import org.apache.ojb.broker.OJBRuntimeException;
import org.apache.ojb.broker.PBKey;
import org.apache.ojb.broker.PersistenceBroker;
@@ -83,7 +82,6 @@
import org.apache.ojb.broker.accesslayer.ConnectionManagerIF;
import org.apache.ojb.broker.accesslayer.IndirectionHandler;
import org.apache.ojb.broker.accesslayer.MaterializationListener;
-import org.apache.ojb.broker.accesslayer.OJBIterator;
import org.apache.ojb.broker.cache.ObjectCache;
import org.apache.ojb.broker.metadata.ClassDescriptor;
import org.apache.ojb.broker.metadata.CollectionDescriptor;
@@ -165,11 +163,11 @@
public void insert(Identity oid, Object obj, int lock)
throws LockingException
{
- insertInternal(oid, obj, lock, true);
+ insertInternal(oid, obj, lock, true, false);
}
- private ContextEntry insertInternal(Identity oid, Object newObj, int lock,
- boolean mainObject)
+ private ContextEntry insertInternal(Identity oid, Object newObj, int lock,
+ boolean mainObject, boolean isDependent)
throws LockingException
{
ContextEntry entry;
@@ -211,7 +209,7 @@
{
Object origObj;
- if (!mainObject || kit.isInsertVerified())
+ if ((!mainObject && !isDependent) || kit.isInsertVerified())
{
origObj = _pb.getObjectByIdentity(oid);
}
@@ -222,19 +220,28 @@
}
if (origObj == null)
{
- entry.state = State.PERSISTENT_NEW;
- if (kit.isEagerInsert(newObj)
- || hasBidirectionalAssociation(newObj.getClass()))
- {
- _pb.store(newObj, entry.state);
- entry.state = State.PERSISTENT_CLEAN;
- origObj = newObj;
+ if (mainObject || isDependent)
+ {
+ entry.state = State.PERSISTENT_NEW;
+ if (kit.isEagerInsert(newObj)
+ || hasBidirectionalAssociation(newObj.getClass()))
+ {
+ _pb.store(newObj, entry.state);
+ entry.state = State.PERSISTENT_CLEAN;
+ origObj = newObj;
+ }
+ }
+ else
+ {
+ // don't create the object by reachability
+ // just invalidate it
+ entry.object = null;
}
}
if (origObj != null)
{
- _original.put(oid, getFields(origObj));
+ _original.put(oid, getFields(origObj, false));
}
}
_objects.put(oid, entry);
@@ -282,8 +289,7 @@
// perform automatic read lock for all reachable objects
// if the inserted object is materialized
- if ((handler == null) && (newObj != null)
- && kit.isImplicitLockingUsed())
+ if ((handler == null) && (newObj != null))
{
lockReachableObjects(newObj);
}
@@ -308,7 +314,7 @@
if (_order.contains(oid)) {
_order.remove(oid);
}
- entry = insertInternal(oid, object, LockType.WRITE_LOCK, true);
+ entry = insertInternal(oid, object, LockType.WRITE_LOCK, true, false);
entry.state = entry.state.deletePersistent();
}
@@ -342,7 +348,12 @@
return;
}
- _original.put(oid, getFields(object));
+ // IndirectionHandler doesn't put the materialized objects
+ // to the cache, I don't know for what reason.
+ // I think it make sense for efficiency
+ _pb.serviceObjectCache().cache(oid, object);
+
+ _original.put(oid, getFields(object, false));
// replace the proxy object with the real one
entry.object = object;
@@ -351,16 +362,13 @@
// perform automatic read lock for all reachable objects
// if the inserted object is materialized
- if (_tx.getKit().isImplicitLockingUsed())
+ try
{
- try
- {
- lockReachableObjects(object);
- }
- catch (LockingException ex)
- {
- throw new LockingPassthruException(ex);
- }
+ lockReachableObjects(object);
+ }
+ catch (LockingException ex)
+ {
+ throw new LockingPassthruException(ex);
}
}
@@ -407,7 +415,7 @@
{
Identity oid = (Identity) iterator.next();
ContextEntry entry = (ContextEntry) _objects.get(oid);
- _checkpointed.put(oid, getFields(entry.object));
+ _checkpointed.put(oid, getFields(entry.object, false));
}
}
@@ -427,12 +435,10 @@
boolean saveBatchMode = connMan.isBatchMode();
Swizzling swizzlingStrategy = _tx.getKit().getSwizzlingStrategy();
LockManager lockManager = LockManager.getInstance();
- Identity[] order = (Identity[]) _order.toArray(new Identity[_order.size()]);
- Identity[] lockOrder = new Identity[order.length];
+ Identity[] lockOrder = (Identity[]) _order.toArray(new Identity[_order.size()]);
ObjectCache cache = _pb.serviceObjectCache();
boolean isInsertVerified = _tx.getKit().isInsertVerified();
- System.arraycopy(order, 0, lockOrder, 0, order.length);
// sort objects in the order of oid.hashCode to avoid deadlocks
Arrays.sort(lockOrder, new Comparator()
{
@@ -449,49 +455,83 @@
try {
// mark dirty objects and lock them for write
- for (int i = 0; i < lockOrder.length; i++)
- {
- Identity oid = lockOrder[i];
- ContextEntry entry = (ContextEntry) _objects.get(oid);
- State state = entry.state;
- IndirectionHandler handler;
-
- if (entry.object == null) // invalidated
+ // also handle dependent objects and if there were inserted once,
+ // repeat this process for their dependants ("cascade create")
+ ArrayList newObjects = new ArrayList();
+ int countNewObjects;
+ do
+ {
+ newObjects.clear();
+ countNewObjects = 0;
+ for (int i = 0; i < lockOrder.length; i++)
{
- continue;
- }
+ Identity oid = lockOrder[i];
+ ContextEntry entry = (ContextEntry) _objects.get(oid);
+ State state = entry.state;
+ IndirectionHandler handler;
- if (entry.handler == null) // materialized
- {
- if (!state.needsUpdate() && !state.needsInsert()
- && !state.needsDelete())
+ if (entry.object == null) // invalidated
{
- Object[][] origFields = (Object[][]) _checkpointed.get(oid);
- Object[][] newFields = getFields(entry.object);
+ continue;
+ }
- if ((origFields == null)
- || isModified(origFields[0], newFields[0]))
+ if (entry.handler == null) // materialized
+ {
+ if (!state.needsDelete())
{
- entry.state = state.markDirty();
- entry.needsCacheSwizzle = true;
- lockManager.ensureLock(oid, _tx, LockType.WRITE_LOCK, _pb);
- }
+ Object[][] origFields = (Object[][]) _checkpointed.get(oid);
+ Object[][] newFields = getFields(entry.object, true);
- if ((origFields != null)
- && isModified(origFields[1], newFields[1]))
- {
- // there are modified collections,
- // so we need to lock the object and to swizzle it to cache
- entry.needsCacheSwizzle = true;
- lockManager.ensureLock(oid, _tx, LockType.WRITE_LOCK, _pb);
+ if (origFields == null)
+ {
+ newObjects.addAll(
+ handleDependentReferences(entry.object,
+ null, newFields[0], newFields[2]));
+ newObjects.addAll(
+ handleDependentCollections(entry.object,
+ null, newFields[1], newFields[3]));
+ }
+ else
+ {
+ if (isModified(origFields[0], newFields[0]))
+ {
+ entry.state = state.markDirty();
+ entry.needsCacheSwizzle = true;
+ lockManager.ensureLock(oid, _tx, LockType.WRITE_LOCK, _pb);
+ newObjects.addAll(
+ handleDependentReferences(entry.object,
+ origFields[0], newFields[0], newFields[2]));
+ }
+
+ if (isModified(origFields[1], newFields[1]))
+ {
+ // there are modified collections,
+ // so we need to lock the object and to swizzle it to cache
+ entry.needsCacheSwizzle = !state.needsInsert();
+ lockManager.ensureLock(oid, _tx, LockType.WRITE_LOCK, _pb);
+ newObjects.addAll(
+ handleDependentCollections(entry.object,
+ origFields[1], newFields[1], newFields[3]));
+ }
+ }
}
}
+ else
+ {
+ entry.handler.removeListener(this);
+ }
}
- else
+ countNewObjects = newObjects.size();
+ if (countNewObjects > 0)
{
- entry.handler.removeListener(this);
+ // new objects are not locked, so we don't need to ensure the order
+ lockOrder = (Identity[]) newObjects.toArray(
+ new Identity[countNewObjects]);
}
}
+ while (countNewObjects > 0);
+
+ Identity[] order = (Identity[]) _order.toArray(new Identity[_order.size()]);
// Swizzle the context objects and the cache objects
for (int i = 0; i < order.length; i++)
@@ -516,6 +556,24 @@
}
}
+ // Cascade delete for dependent objects
+ int countCascadeDeleted;
+ do
+ {
+ countCascadeDeleted = 0;
+ for (int i = 0; i < order.length; i++)
+ {
+ Identity oid = order[i];
+ ContextEntry entry = (ContextEntry) _objects.get(oid);
+
+ if (entry.state.needsDelete())
+ {
+ countCascadeDeleted += doCascadeDelete(entry.object);
+ }
+ }
+ }
+ while (countCascadeDeleted > 0);
+
// perform database operations
connMan.setBatchMode(true);
try
@@ -617,6 +675,7 @@
private void lockReachableObjects(Object object)
throws LockingException
{
+ boolean onlyDependants = !_tx.getKit().isImplicitLockingUsed();
ClassDescriptor mif = _pb.getClassDescriptor(object.getClass());
// N:1 relations
@@ -628,13 +687,18 @@
while (iter.hasNext())
{
rds = (ObjectReferenceDescriptor) iter.next();
+ if (onlyDependants && !rds.getOtmDependent())
+ {
+ continue;
+ }
relObj = rds.getPersistentField().get(object);
if (relObj != null)
{
relOid = new Identity(relObj, _pb);
if (!_order.contains(relOid))
{
- insertInternal(relOid, relObj, LockType.READ_LOCK, false);
+ insertInternal(relOid, relObj, LockType.READ_LOCK, false,
+ rds.getOtmDependent());
}
}
}
@@ -648,14 +712,14 @@
while (collections.hasNext())
{
collectionDescriptor = (CollectionDescriptor) collections.next();
+ if (onlyDependants && !collectionDescriptor.getOtmDependent())
+ {
+ continue;
+ }
col = collectionDescriptor.getPersistentField().get(object);
if (col != null)
{
- if (col instanceof ManageableCollection)
- {
- colIterator = ((ManageableCollection) col).ojbIterator();
- }
- else if (col instanceof Collection)
+ if (col instanceof Collection)
{
colIterator = ((Collection) col).iterator();
}
@@ -667,7 +731,7 @@
{
throw new OJBRuntimeException(
col.getClass()
- + " can not be managed by OJB, use Array, Collection or ManageableCollection instead !");
+ + " can not be managed by OJB OTM, use Array or Collection instead !");
}
while (colIterator.hasNext())
@@ -676,19 +740,14 @@
relOid = new Identity(relObj, _pb);
if (!_order.contains(relOid))
{
- insertInternal(relOid, relObj, LockType.READ_LOCK, false);
+ insertInternal(relOid, relObj, LockType.READ_LOCK, false,
+ collectionDescriptor.getOtmDependent());
}
}
-
- if (colIterator instanceof OJBIterator)
- {
- ((OJBIterator) colIterator).releaseDbResources();
- }
}
}
}
-
private void releaseLocks()
{
LockManager lockManager = LockManager.getInstance();
@@ -741,22 +800,33 @@
return false;
}
-
+
/**
- * Two arrays of field values:
- * 1) The class, simple fields, references
- * 2) collections
+ * @return four arrays of field values:
+ * 1) The class, simple fields, identities of references
+ * 2) collections of identities
+ * 3) references (parallel to identities of references in 1)
+ * 4) collections of objects (parallel to collections of identities in 2)
+ * if "withObjects" parameter is "false", then returns nulls
+ * in places of 3) and 4)
*/
- private Object[][] getFields(Object obj)
+ private Object[][] getFields(Object obj, boolean withObjects)
{
ClassDescriptor mif = _pb.getClassDescriptor(obj.getClass());
FieldDescriptor[] fieldDescs = mif.getFieldDescriptions();
- Collection refDescsCol = mif.getObjectReferenceDescriptors();
Collection refDescs = mif.getObjectReferenceDescriptors();
Collection colDescs = mif.getCollectionDescriptors();
int count = 0;
Object[] fields = new Object[1 + fieldDescs.length + refDescs.size()];
ArrayList[] collections = new ArrayList[colDescs.size()];
+ Object[] references = null;
+ ArrayList[] collectionsOfObjects = null;
+
+ if (withObjects)
+ {
+ references = new Object[refDescs.size()];
+ collectionsOfObjects = new ArrayList[colDescs.size()];
+ }
fields[0] = obj.getClass(); // we must notice if the object class changes
count++;
@@ -769,7 +839,8 @@
count++;
}
- for (Iterator it = refDescs.iterator(); it.hasNext(); count++)
+ int countRefs = 0;
+ for (Iterator it = refDescs.iterator(); it.hasNext(); count++, countRefs++)
{
ObjectReferenceDescriptor rds = (ObjectReferenceDescriptor) it.next();
PersistentField f = rds.getPersistentField();
@@ -777,6 +848,10 @@
if (relObj != null)
{
fields[count] = new Identity(relObj, _pb);
+ if (withObjects)
+ {
+ references[countRefs] = relObj;
+ }
}
}
@@ -790,9 +865,16 @@
if (col != null)
{
ArrayList list = new ArrayList();
+ ArrayList listOfObjects = null;
Iterator colIterator;
collections[count] = list;
+ if (withObjects)
+ {
+ listOfObjects = new ArrayList();
+ collectionsOfObjects[count] = listOfObjects;
+ }
+
if (Collection.class.isAssignableFrom(type))
{
colIterator = ((Collection) col).iterator();
@@ -808,12 +890,17 @@
while (colIterator.hasNext())
{
- list.add(new Identity(colIterator.next(), _pb));
+ Object relObj = colIterator.next();
+ list.add(new Identity(relObj, _pb));
+ if (withObjects)
+ {
+ listOfObjects.add(relObj);
+ }
}
}
}
- return new Object[][] {fields, collections};
+ return new Object[][] {fields, collections, references, collectionsOfObjects};
}
private void setFields(Object obj, Object[][] fieldsAndCollections)
@@ -990,10 +1077,225 @@
return hasBidirAssc;
}
+ /**
+ * @return number of deleted objects: 1 or 0 (if the object is already deleted)
+ */
+ private int markDelete(Object oid)
+ {
+ ContextEntry entry = (ContextEntry) _objects.get(oid);
+
+ if (entry == null)
+ {
+ throw new IllegalStateException("markDelete failed: the dependent object "
+ + oid + " is not in the editing context");
+ }
+
+ if (entry.state.needsDelete())
+ {
+ return 0;
+ }
+ else
+ {
+ entry.state = entry.state.deletePersistent();
+ return 1;
+ }
+ }
+
+ /**
+ * Insert the object is not in the context and mark as new.
+ */
+ private void markNew(Object oid, Object object)
+ {
+ ContextEntry entry = new ContextEntry(object);
+
+ if (entry.handler != null)
+ {
+ throw new IllegalStateException("markNew failed: the dependent object "
+ + oid + " is proxy");
+ }
+
+ entry.state = State.PERSISTENT_NEW;
+ _objects.put(oid, entry);
+ _order.add(oid);
+ }
+
+ /**
+ * Mark for creation all newly introduced dependent references.
+ * Mark for deletion all nullified dependent references.
+ * @return the list of created objects
+ */
+ private ArrayList handleDependentReferences(Object obj,
+ Object[] origFields, Object[] newFields, Object[] newRefs)
+ {
+ ClassDescriptor mif = _pb.getClassDescriptor(obj.getClass());
+ FieldDescriptor[] fieldDescs = mif.getFieldDescriptions();
+ Collection refDescs = mif.getObjectReferenceDescriptors();
+ int count = 1 + fieldDescs.length;
+ ArrayList newObjects = new ArrayList();
+ int countRefs = 0;
+
+ for (Iterator it = refDescs.iterator(); it.hasNext(); count++, countRefs++)
+ {
+ ObjectReferenceDescriptor rds = (ObjectReferenceDescriptor) it.next();
+ Object origOid = (origFields == null ? null : origFields[count]);
+ Object newOid = newFields[count];
+
+ if (rds.getOtmDependent())
+ {
+ if ((origOid == null) && (newOid != null))
+ {
+ ContextEntry entry = (ContextEntry) _objects.get(newOid);
+
+ if (entry == null)
+ {
+ Object relObj = newRefs[countRefs];
+ markNew(newOid, relObj);
+ newObjects.add(newOid);
+ }
+ }
+ else if ((origOid != null) && (newOid == null))
+ {
+ markDelete(origOid);
+ }
+ }
+ }
+
+ return newObjects;
+ }
+
+ /**
+ * Mark for creation all objects that were included into dependent collections.
+ * Mark for deletion all objects that were excluded from dependent collections.
+ */
+ private ArrayList handleDependentCollections(Object obj,
+ Object[] origCollections, Object[] newCollections,
+ Object[] newCollectionsOfObjects)
+ {
+ ClassDescriptor mif = _pb.getClassDescriptor(obj.getClass());
+ Collection colDescs = mif.getCollectionDescriptors();
+ ArrayList newObjects = new ArrayList();
+ int count = 0;
+
+ for (Iterator it = colDescs.iterator(); it.hasNext(); count++)
+ {
+ CollectionDescriptor cds = (CollectionDescriptor) it.next();
+
+ if (cds.getOtmDependent())
+ {
+ ArrayList origList = (origCollections == null ? null
+ : (ArrayList) origCollections[count]);
+ ArrayList newList = (ArrayList) newCollections[count];
+
+ if (origList != null)
+ {
+ for (Iterator it2 = origList.iterator(); it2.hasNext(); )
+ {
+ Object origOid = it2.next();
+
+ if (!newList.contains(origOid))
+ {
+ markDelete(origOid);
+ }
+ }
+ }
+
+ int countElem = 0;
+ for (Iterator it2 = newList.iterator(); it2.hasNext(); countElem++)
+ {
+ Object newOid = it2.next();
+
+ if ((origList == null) || !origList.contains(newOid))
+ {
+ ContextEntry entry = (ContextEntry) _objects.get(newOid);
+
+ if (entry == null)
+ {
+ ArrayList relCol = (ArrayList)
+ newCollectionsOfObjects[count];
+ Object relObj = relCol.get(countElem);
+ markNew(newOid, relObj);
+ newObjects.add(newOid);
+ }
+ }
+ }
+ }
+ }
+
+ return newObjects;
+ }
+
+ /**
+ * Mark for deletion all dependent objects (via references and collections).
+ * @return the number of deleted objects
+ */
+ private int doCascadeDelete(Object obj)
+ {
+ ClassDescriptor mif = _pb.getClassDescriptor(obj.getClass());
+ Collection refDescs = mif.getObjectReferenceDescriptors();
+ Collection colDescs = mif.getCollectionDescriptors();
+ int countCascadeDeleted = 0;
+
+ for (Iterator it = refDescs.iterator(); it.hasNext(); )
+ {
+ ObjectReferenceDescriptor rds = (ObjectReferenceDescriptor) it.next();
+
+ if (rds.getOtmDependent())
+ {
+ PersistentField f = rds.getPersistentField();
+ Object relObj = f.get(obj);
+
+ if (relObj != null)
+ {
+ countCascadeDeleted +=
+ markDelete(new Identity(relObj, _pb));
+ }
+ }
+ }
+
+ for (Iterator it = colDescs.iterator(); it.hasNext(); )
+ {
+ CollectionDescriptor cds = (CollectionDescriptor) it.next();
+
+ if (cds.getOtmDependent())
+ {
+ PersistentField f = cds.getPersistentField();
+ Class type = f.getType();
+ Object col = f.get(obj);
+
+ if (col != null)
+ {
+ Iterator colIterator;
+
+ if (Collection.class.isAssignableFrom(type))
+ {
+ colIterator = ((Collection) col).iterator();
+ }
+ else if (type.isArray())
+ {
+ colIterator = new ArrayIterator(col);
+ }
+ else
+ {
+ continue;
+ }
+
+ while (colIterator.hasNext())
+ {
+
+ countCascadeDeleted +=
+ markDelete(new Identity(colIterator.next(), _pb));
+ }
+ }
+ }
+ }
+
+ return countCascadeDeleted;
+ }
+
/*
* The rest of ObjectCache implementation for swizling
* All methods except lookup() are never used by swizzling,
- * remove() appeared to already exist in this class
+ * remove() appeared to already exist in this class
* with the same signature as in ObjectCache interface,
* other methods are unsupported.
*/
@@ -1021,7 +1323,7 @@
* Handler the proxy object, null if the object is real
*/
IndirectionHandler handler;
-
+
/**
* This flag is used during commit/checkpoint
*/
1.43 +40 -0 db-ojb/src/schema/ojbtest-schema.xml
Index: ojbtest-schema.xml
===================================================================
RCS file: /home/cvs/db-ojb/src/schema/ojbtest-schema.xml,v
retrieving revision 1.42
retrieving revision 1.43
diff -u -r1.42 -r1.43
--- ojbtest-schema.xml 8 Jul 2003 00:34:54 -0000 1.42
+++ ojbtest-schema.xml 24 Jul 2003 21:55:03 -0000 1.43
@@ -742,4 +742,44 @@
<column name="NESTED_DESCRIPTION" type="VARCHAR" size="150"/>
</table>
+ <!-- ************************************************* -->
+ <!-- Classes for OTM dependent objects test -->
+ <!-- ************************************************* -->
+
+ <table name="OTM_PERSON"
+ javaName="OtmPerson">
+ <column name="ID" required="true" primaryKey="true" type="INTEGER"
+ javaName="id"/>
+ <column name="FIRSTNAME" type="VARCHAR" size="60"
+ javaName="firstname"/>
+ <column name="LASTNAME" type="VARCHAR" size="60"
+ javaName="lastname"/>
+ <column name="MAIN_ADDRESS_ID" type="INTEGER" required="false"
+ javaName="mainAddressId"/>
+ </table>
+
+ <table name="OTM_ADDRESS"
+ javaName="OtmAddress">
+ <column name="ID" required="true" primaryKey="true" type="INTEGER"
+ javaName="id"/>
+ <column name="COUNTRY" type="VARCHAR" size="60"
+ javaName="country"/>
+ <column name="CITY" type="VARCHAR" size="60"
+ javaName="city"/>
+ <column name="STREET" type="VARCHAR" size="60"
+ javaName="street"/>
+ </table>
+
+ <table name="OTM_ADDRESS_DESC"
+ javaName="OtmAddressDesc">
+ <column name="ID" required="true" primaryKey="true" type="INTEGER"
+ javaName="id"/>
+ <column name="DESC" type="VARCHAR" size="60"
+ javaName="desc"/>
+ <column name="PERSON_ID" type="INTEGER" required="true"
+ javaName="personId" />
+ <column name="ADDRESS_ID" type="INTEGER" required="true"
+ javaName="addressId"/>
+ </table>
+
</database>
1.43 +17 -0 db-ojb/src/test/org/apache/ojb/repository.dtd
Index: repository.dtd
===================================================================
RCS file: /home/cvs/db-ojb/src/test/org/apache/ojb/repository.dtd,v
retrieving revision 1.42
retrieving revision 1.43
diff -u -r1.42 -r1.43
--- repository.dtd 22 Jul 2003 21:08:09 -0000 1.42
+++ repository.dtd 24 Jul 2003 21:55:04 -0000 1.43
@@ -522,6 +522,12 @@
this reference attribute on deleting the persistent object.
This attribute must be set to false if using the OTM, ODMG or JDO layer.
+ The otm-dependent attribute specifies whether the OTM layer automatically
+ creates the referred object or deletes it if the reference field is set to null.
+ Also otm-dependent references behave as if auto-update and auto-delete
+ were set to true, but the auto-update and auto-delete attributes themself
+ must be always set to false for use with OTM layer.
+
-->
<!ATTLIST reference-descriptor
name CDATA #REQUIRED
@@ -533,6 +539,7 @@
auto-retrieve (true | false) "true"
auto-update (true | false) "false"
auto-delete (true | false) "false"
+ otm-dependent (true | false) "false"
>
<!--
@@ -619,6 +626,15 @@
The auto-delete attribute specifies whether OJB automatically deletes
this reference attribute on deleting the persistent object.
This attribute must be set to false if using the OTM, ODMG or JDO layer.
+
+ The otm-dependent attribute specifies whether the OTM layer automatically
+ creates collection elements that were included into the collectionelements
+ and deletes collection elements that were excluded from the collection.
+ Also otm-dependent references behave as if auto-update and auto-delete
+ were set to true, but the auto-update and auto-delete attributes themself
+ must be always set to false for use with OTM layer.
+
+
-->
<!ATTLIST collection-descriptor
name CDATA #IMPLIED
@@ -635,6 +651,7 @@
auto-retrieve (true | false) "true"
auto-update (true | false) "false"
auto-delete (true | false) "false"
+ otm-dependent (true | false) "false"
>
<!--
1.81 +116 -1 db-ojb/src/test/org/apache/ojb/repository_junit.xml
Index: repository_junit.xml
===================================================================
RCS file: /home/cvs/db-ojb/src/test/org/apache/ojb/repository_junit.xml,v
retrieving revision 1.80
retrieving revision 1.81
diff -u -r1.80 -r1.81
--- repository_junit.xml 11 Jul 2003 07:43:53 -0000 1.80
+++ repository_junit.xml 24 Jul 2003 21:55:04 -0000 1.81
@@ -4418,6 +4418,121 @@
/>
</class-descriptor>
+ <!-- ************************************************* -->
+ <!-- Classes for OTM dependent objects test -->
+ <!-- ************************************************* -->
+ <class-descriptor
+ class="org.apache.ojb.otm.Person"
+ table="OTM_PERSON"
+ >
+ <field-descriptor
+ name="id"
+ column="ID"
+ jdbc-type="INTEGER"
+ primarykey="true"
+ autoincrement="true"
+ />
+ <field-descriptor
+ name="firstname"
+ column="FIRSTNAME"
+ jdbc-type="VARCHAR"
+ />
+ <field-descriptor
+ name="lastname"
+ column="LASTNAME"
+ jdbc-type="VARCHAR"
+ />
+ <field-descriptor
+ name="mainAddressId"
+ column="MAIN_ADDRESS_ID"
+ jdbc-type="INTEGER"
+ />
+ <reference-descriptor
+ name="mainAddress"
+ class-ref="org.apache.ojb.otm.Address"
+ otm-dependent="true"
+ >
+ <foreignkey field-ref="mainAddressId"/>
+ </reference-descriptor>
+ <collection-descriptor
+ name="otherAddresses"
+ element-class-ref="org.apache.ojb.otm.AddressDesc"
+ collection-class="org.apache.ojb.broker.util.collections.ManageableArrayList"
+ otm-dependent="true"
+ >
+ <inverse-foreignkey field-ref="personId"/>
+ </collection-descriptor>
+ </class-descriptor>
-<!-- Mapping of classes used in junit tests and tutorials ends here -->
\ No newline at end of file
+ <class-descriptor
+ class="org.apache.ojb.otm.Address"
+ table="OTM_ADDRESS"
+ >
+ <field-descriptor
+ name="id"
+ column="ID"
+ jdbc-type="INTEGER"
+ primarykey="true"
+ autoincrement="true"
+ />
+ <field-descriptor
+ name="country"
+ column="COUNTRY"
+ jdbc-type="VARCHAR"
+ />
+ <field-descriptor
+ name="city"
+ column="CITY"
+ jdbc-type="VARCHAR"
+ />
+ <field-descriptor
+ name="street"
+ column="STREET"
+ jdbc-type="VARCHAR"
+ />
+ </class-descriptor>
+
+ <class-descriptor
+ class="org.apache.ojb.otm.AddressDesc"
+ table="OTM_ADDRESS_DESC"
+ >
+ <field-descriptor
+ name="id"
+ column="ID"
+ jdbc-type="INTEGER"
+ primarykey="true"
+ autoincrement="true"
+ />
+ <field-descriptor
+ name="desc"
+ column="DESC"
+ jdbc-type="VARCHAR"
+ />
+ <field-descriptor
+ name="personId"
+ column="PERSON_ID"
+ jdbc-type="INTEGER"
+ />
+ <field-descriptor
+ name="addressId"
+ column="ADDRESS_ID"
+ jdbc-type="INTEGER"
+ />
+ <reference-descriptor
+ name="person"
+ class-ref="org.apache.ojb.otm.Person"
+ >
+ <foreignkey field-ref="personId"/>
+ </reference-descriptor>
+ <reference-descriptor
+ name="address"
+ class-ref="org.apache.ojb.otm.Address"
+ otm-dependent="true"
+ >
+ <foreignkey field-ref="addressId"/>
+ </reference-descriptor>
+ </class-descriptor>
+
+
+<!-- Mapping of classes used in junit tests and tutorials ends here -->
1.9 +1 -0 db-ojb/src/test/org/apache/ojb/otm/AllTests.java
Index: AllTests.java
===================================================================
RCS file: /home/cvs/db-ojb/src/test/org/apache/ojb/otm/AllTests.java,v
retrieving revision 1.8
retrieving revision 1.9
diff -u -r1.8 -r1.9
--- AllTests.java 7 Jul 2003 22:54:37 -0000 1.8
+++ AllTests.java 24 Jul 2003 21:55:04 -0000 1.9
@@ -36,6 +36,7 @@
suite.addTest(new TestSuite(LockTestSerializable.class));
suite.addTest(new TestSuite(SwizzleTests.class));
suite.addTest(new TestSuite(CopyTest.class));
+ suite.addTest(new TestSuite(DependentTests.class));
return suite;
}
1.1 db-ojb/src/test/org/apache/ojb/otm/Address.java
Index: Address.java
===================================================================
/*
* ObJectRelationalBridge - Bridging Java Objects and Relational Databases
* http://objectbridge.sourceforge.net
* Copyright (C) 2000, 2001 Thomas Mahler, et al.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.apache.ojb.otm;
import java.io.Serializable;
public class Address implements Serializable
{
private int id;
private String country;
private String city;
private String street;
public Address()
{
}
public Address(String country, String city, String street)
{
this.country = country;
this.city = city;
this.street = street;
}
public int getId()
{
return id;
}
public void setId(int id)
{
this.id = id;
}
public String getCountry()
{
return country;
}
public void setCountry(String country)
{
this.country = country;
}
public String getCity()
{
return city;
}
public void setCity(String city)
{
this.city = city;
}
public String getStreet()
{
return street;
}
public void setStreet(String street)
{
this.street = street;
}
}
1.1 db-ojb/src/test/org/apache/ojb/otm/AddressDesc.java
Index: AddressDesc.java
===================================================================
/*
* ObJectRelationalBridge - Bridging Java Objects and Relational Databases
* http://objectbridge.sourceforge.net
* Copyright (C) 2000, 2001 Thomas Mahler, et al.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.apache.ojb.otm;
import java.io.Serializable;
public class AddressDesc implements Serializable
{
private int id;
private String desc;
private int personId;
private Person person;
private int addressId;
private Address address;
public AddressDesc()
{
}
public AddressDesc(String desc, Address address)
{
this.desc = desc;
this.address = address;
}
public int getId()
{
return id;
}
public void setId(int id)
{
this.id = id;
}
public String getDesc()
{
return desc;
}
public void setDesc(String desc)
{
this.desc = desc;
}
public int getPersonId()
{
return personId;
}
public void setPersonId(int personId)
{
this.personId = personId;
}
public Person getPerson()
{
return person;
}
public void setPerson(Person person)
{
this.person = person;
}
public int getAddressId()
{
return addressId;
}
public void setAddressId(int addressId)
{
this.addressId = addressId;
}
public Address getAddress()
{
return address;
}
public void setAddress(Address address)
{
this.address = address;
}
}
1.1 db-ojb/src/test/org/apache/ojb/otm/DependentTests.java
Index: DependentTests.java
===================================================================
package org.apache.ojb.otm;
import java.util.Iterator;
import junit.framework.TestCase;
import org.apache.ojb.broker.Identity;
import org.apache.ojb.broker.PersistenceBrokerFactory;
import org.apache.ojb.broker.query.Criteria;
import org.apache.ojb.broker.query.Query;
import org.apache.ojb.broker.query.QueryFactory;
import org.apache.ojb.otm.core.Transaction;
import org.apache.ojb.otm.core.TransactionException;
import org.apache.ojb.otm.lock.LockingException;
public class DependentTests extends TestCase
{
private static Class CLASS = DependentTests.class;
private TestKit _kit;
private OTMConnection _conn;
public void setUp() throws LockingException
{
_kit = TestKit.getTestInstance();
_conn = _kit.acquireConnection(PersistenceBrokerFactory.getDefaultKey());
}
public void tearDown() throws LockingException
{
_conn.close();
_conn = null;
}
public static void main(String[] args)
{
String[] arr = {CLASS.getName()};
junit.textui.TestRunner.main(arr);
}
public void testDependent() throws Exception
{
Person person = new Person("Ostap", "Bender");
Address address1 = new Address("Ukraine", "Odessa", "Deribasovskaya");
Address address2 = new Address("Ukraine", "Odessa", "Malaya Arnautskaya");
Address address3 = new Address("Brasil", "Rio de Janeiro", "Rua Professor Azevedo Marques");
Criteria emptyCriteria = new Criteria();
Query q;
Iterator it;
person.setMainAddress(address1);
person.addOtherAddress("work", address2);
person.addOtherAddress("dream", address3);
Transaction tx = _kit.getTransaction(_conn);
tx.begin();
// Cascade create
_conn.makePersistent(person);
tx.commit();
Identity oid = _conn.getIdentity(person);
_conn.invalidateAll();
tx = _kit.getTransaction(_conn);
tx.begin();
person = (Person) _conn.getObjectByIdentity(oid);
assertTrue("person exists", (person != null));
assertTrue("main Address exists", (person.getMainAddress() != null));
assertEquals("main Address is correct", address1.getStreet(), person.getMainAddress().getStreet());
assertEquals("two other Addresses", 2, person.getOtherAddresses().size());
AddressDesc desc1 = (AddressDesc) person.getOtherAddresses().get(0);
assertEquals("1st other Address has correct description", "work", desc1.getDesc());
assertEquals("1st other Address is correct", address2.getStreet(), desc1.getAddress().getStreet());
AddressDesc desc2 = (AddressDesc) person.getOtherAddresses().get(1);
assertEquals("2nd other Address has correct description", "dream", desc2.getDesc());
assertEquals("2nd other Address is correct", address3.getStreet(), desc2.getAddress().getStreet());
// Delete dependent
person.setMainAddress(null);
person.getOtherAddresses().remove(1);
tx.commit();
_conn.invalidateAll();
tx = _kit.getTransaction(_conn);
tx.begin();
person = (Person) _conn.getObjectByIdentity(oid);
assertTrue("main Address doesn't exist", (person.getMainAddress() == null));
assertEquals("one other Address", 1, person.getOtherAddresses().size());
desc2 = (AddressDesc) person.getOtherAddresses().get(0);
assertEquals("the other Address has correct description", "work", desc1.getDesc());
assertEquals("the other Address is correct", address2.getStreet(), desc1.getAddress().getStreet());
// Create dependent
person.setMainAddress(address1);
person.addOtherAddress("dream", address3);
tx.commit();
_conn.invalidateAll();
tx = _kit.getTransaction(_conn);
tx.begin();
person = (Person) _conn.getObjectByIdentity(oid);
assertTrue("main Address exists", (person.getMainAddress() != null));
assertEquals("main Address is correct", address1.getStreet(), person.getMainAddress().getStreet());
assertEquals("two other Addresses", 2, person.getOtherAddresses().size());
desc1 = (AddressDesc) person.getOtherAddresses().get(0);
assertEquals("1st other Address has correct description", "work", desc1.getDesc());
assertEquals("1st other Address is correct", address2.getStreet(), desc1.getAddress().getStreet());
desc2 = (AddressDesc) person.getOtherAddresses().get(1);
assertEquals("2nd other Address has correct description", "dream", desc2.getDesc());
assertEquals("2nd other Address is correct", address3.getStreet(), desc2.getAddress().getStreet());
// Cascade delete
_conn.deletePersistent(person);
tx.commit();
_conn.invalidateAll();
tx = _kit.getTransaction(_conn);
tx.begin();
person = (Person) _conn.getObjectByIdentity(oid);
assertTrue("person doesn't exist", (person == null));
q = QueryFactory.newQuery(AddressDesc.class, emptyCriteria);
it = _conn.getIteratorByQuery(q);
assertTrue("address descriptions don't exist", !it.hasNext());
q = QueryFactory.newQuery(Address.class, emptyCriteria);
it = _conn.getIteratorByQuery(q);
assertTrue("addresses don't exist", !it.hasNext());
tx.commit();
}
}
1.1 db-ojb/src/test/org/apache/ojb/otm/Person.java
Index: Person.java
===================================================================
/*
* ObJectRelationalBridge - Bridging Java Objects and Relational Databases
* http://objectbridge.sourceforge.net
* Copyright (C) 2000, 2001 Thomas Mahler, et al.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.apache.ojb.otm;
import java.util.ArrayList;
import java.io.Serializable;
public class Person implements Serializable
{
private int id;
private String firstname;
private String lastname;
private Integer mainAddressId;
private Address mainAddress;
private ArrayList otherAddresses;
public Person()
{
}
public Person(String firstname, String lastname)
{
this.firstname = firstname;
this.lastname = lastname;
}
public int getId()
{
return id;
}
public void setId(int id)
{
this.id = id;
}
public String getFirstname()
{
return firstname;
}
public void setFirstname(String firstname)
{
this.firstname = firstname;
}
public String getLastname()
{
return lastname;
}
public void setLastname(String lastname)
{
this.lastname = lastname;
}
public Integer getMainAddressId()
{
return mainAddressId;
}
public void setMainAddressId(Integer mainAddressId)
{
this.mainAddressId = mainAddressId;
}
public Address getMainAddress()
{
return mainAddress;
}
public void setMainAddress(Address mainAddress)
{
this.mainAddress = mainAddress;
}
public ArrayList getOtherAddresses()
{
return otherAddresses;
}
public void setOtherAddresses(ArrayList otherAddresses)
{
this.otherAddresses = otherAddresses;
}
public void addOtherAddress(String desc, Address address)
{
if (otherAddresses == null)
{
otherAddresses = new ArrayList();
}
AddressDesc addrDesc = new AddressDesc(desc, address);
this.otherAddresses.add(addrDesc);
addrDesc.setPerson(this);
}
}
---------------------------------------------------------------------
To unsubscribe, e-mail: ojb-dev-unsubscribe@db.apache.org
For additional commands, e-mail: ojb-dev-help@db.apache.org