diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/StoreCollectionFieldStrategy.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/StoreCollectionFieldStrategy.java
index 4cffba18a..504ae6c1b 100644
--- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/StoreCollectionFieldStrategy.java
+++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/StoreCollectionFieldStrategy.java
@@ -40,6 +40,7 @@ import org.apache.openjpa.jdbc.sql.Select;
import org.apache.openjpa.jdbc.sql.SelectExecutor;
import org.apache.openjpa.jdbc.sql.Union;
import org.apache.openjpa.kernel.OpenJPAStateManager;
+import org.apache.openjpa.kernel.StateManagerImpl;
import org.apache.openjpa.lib.util.Localizer;
import org.apache.openjpa.meta.ClassMetaData;
import org.apache.openjpa.meta.JavaTypes;
@@ -345,8 +346,9 @@ public abstract class StoreCollectionFieldStrategy
if (decMeta == null)
return;
- if (oid.equals(sm.getObjectId())) {
- mappedByValue = sm.getPersistenceCapable();
+ StateManagerImpl owner = ((StateManagerImpl)sm).getObjectIdOwner();
+ if (oid.equals(owner.getObjectId())) {
+ mappedByValue = owner.getPersistenceCapable();
res.setMappedByFieldMapping(mappedByFieldMapping);
res.setMappedByValue(mappedByValue);
} else if (coll instanceof Collection &&
diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/InverseManager.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/InverseManager.java
index 2c89e0766..457af24fc 100644
--- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/InverseManager.java
+++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/InverseManager.java
@@ -18,16 +18,21 @@
*/
package org.apache.openjpa.kernel;
+import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.Iterator;
+import java.util.Map;
import java.util.Set;
import org.apache.openjpa.conf.OpenJPAConfiguration;
import org.apache.openjpa.datacache.DataCache;
+import org.apache.openjpa.enhance.PersistenceCapable;
import org.apache.openjpa.lib.conf.Configurable;
import org.apache.openjpa.lib.conf.Configuration;
import org.apache.openjpa.lib.log.Log;
import org.apache.openjpa.lib.util.Localizer;
+import org.apache.openjpa.meta.ClassMetaData;
import org.apache.openjpa.meta.FieldMetaData;
import org.apache.openjpa.meta.JavaTypes;
import org.apache.openjpa.meta.ValueMetaData;
@@ -131,7 +136,8 @@ public class InverseManager implements Configurable {
public void correctRelations(OpenJPAStateManager sm, FieldMetaData fmd,
Object value) {
if (fmd.getDeclaredTypeCode() != JavaTypes.PC &&
- (fmd.getDeclaredTypeCode() != JavaTypes.COLLECTION ||
+ ((fmd.getDeclaredTypeCode() != JavaTypes.COLLECTION &&
+ fmd.getDeclaredTypeCode() != JavaTypes.MAP) ||
fmd.getElement().getDeclaredTypeCode() != JavaTypes.PC))
return;
@@ -146,23 +152,43 @@ public class InverseManager implements Configurable {
// clear any restorable relations
clearInverseRelations(sm, fmd, inverses, value);
- if (value != null) {
- StoreContext ctx = sm.getContext();
- switch (fmd.getDeclaredTypeCode()) {
- case JavaTypes.PC:
- createInverseRelations(ctx, sm.getManagedInstance(),
- value, fmd, inverses);
- break;
- case JavaTypes.COLLECTION:
- for (Iterator itr = ((Collection) value).iterator();
- itr.hasNext();)
- createInverseRelations(ctx, sm.getManagedInstance(),
- itr.next(), fmd, inverses);
- break;
- }
- }
+ // create inverse relations
+ createInverseRelations(sm, fmd, inverses, value);
}
+ protected void createInverseRelations(OpenJPAStateManager sm,
+ FieldMetaData fmd, FieldMetaData[] inverses, Object value) {
+ if (value == null)
+ return;
+ StoreContext ctx = sm.getContext();
+ if (isEmbedded(sm))
+ return;
+ Object obj = sm.getManagedInstance();
+ createInverseRelations(ctx, obj, fmd, inverses, value);
+ }
+
+ protected void createInverseRelations(StoreContext ctx, Object obj,
+ FieldMetaData fmd, FieldMetaData[] inverses, Object value) {
+ switch (fmd.getDeclaredTypeCode()) {
+ case JavaTypes.PC:
+ createInverseRelations(ctx, obj, value, fmd, inverses);
+ break;
+ case JavaTypes.COLLECTION:
+ for (Iterator itr = ((Collection) value).iterator();
+ itr.hasNext();)
+ createInverseRelations(ctx, obj, itr.next(), fmd, inverses);
+ break;
+ }
+ }
+
+ protected boolean isEmbedded(OpenJPAStateManager sm) {
+ ClassMetaData meta = sm.getMetaData();
+ ValueMetaData owner = meta.getEmbeddingMetaData();
+ if (owner != null)
+ return true;
+ return false;
+ }
+
/**
* Create the inverse relations for all the given inverse fields.
* A relation exists from fromRef
to toRef
; this
@@ -241,13 +267,22 @@ public class InverseManager implements Configurable {
Object initial = sm.fetchInitialField(fmd.getIndex());
clearInverseRelations(sm, initial, fmd, inverses);
} else {
- Collection initial = (Collection)
- sm.fetchInitialField(fmd.getIndex());
+ Object obj = sm.fetchInitialField(fmd.getIndex());
+ Collection initial = null;
+ if (obj instanceof Collection)
+ initial = (Collection) obj;
+ else if (obj instanceof Map)
+ initial = ((Map)obj).values();
+
if (initial == null)
return;
// clear all relations not also in the new value
- Collection coll = (Collection) newValue;
+ Collection coll = null;
+ if (newValue instanceof Collection)
+ coll = (Collection) newValue;
+ else if (newValue instanceof Map)
+ coll = ((Map)newValue).values();
Object elem;
for (Iterator itr = initial.iterator(); itr.hasNext();) {
elem = itr.next();
@@ -285,7 +320,10 @@ public class InverseManager implements Configurable {
case JavaTypes.PC:
if (!owned || inverses[i].getCascadeDelete()
== ValueMetaData.CASCADE_AUTO)
- storeNull(other, inverses[i], sm.getManagedInstance());
+ if (fmd.getOrphanRemoval() || fmd.getElement().getOrphanRemoval())
+ ((StateManagerImpl)other).delete();
+ else
+ storeNull(other, inverses[i], sm.getManagedInstance());
break;
case JavaTypes.COLLECTION:
if (!owned || inverses[i].getElement().getCascadeDelete()
diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/OrphanRemovalManager.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/OrphanRemovalManager.java
new file mode 100644
index 000000000..feb1a529e
--- /dev/null
+++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/OrphanRemovalManager.java
@@ -0,0 +1,139 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.openjpa.kernel;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.apache.openjpa.meta.FieldMetaData;
+import org.apache.openjpa.meta.JavaTypes;
+
+/**
+ * Class which manages orphanRemoval before flushing
+ * to the datastore.
+ *
+ * @author Fay Wang
+ */
+public class OrphanRemovalManager {
+
+ /**
+ * Correct relations from the given dirty field to inverse instances.
+ * Field fmd
of the instance managed by sm
has
+ * value value
. Ensure that all inverses relations from
+ * value
are consistent with this.
+ */
+ public static void correctRelations(OpenJPAStateManager sm, FieldMetaData fmd,
+ Object value) {
+ if (fmd.getDeclaredTypeCode() != JavaTypes.PC &&
+ ((fmd.getDeclaredTypeCode() != JavaTypes.COLLECTION &&
+ fmd.getDeclaredTypeCode() != JavaTypes.MAP) ||
+ fmd.getElement().getDeclaredTypeCode() != JavaTypes.PC))
+ return;
+
+ FieldMetaData[] orphanRemoves = fmd.getOrphanRemovalMetaDatas();
+ if (orphanRemoves.length == 0)
+ return;
+
+ // clear any restorable relations
+ clearOrphanRemovalRelations(sm, fmd, orphanRemoves, value);
+ }
+
+
+ /**
+ * Remove all relations between the initial value of fmd
for
+ * the instance managed by sm
and its inverses. Relations
+ * shared with newValue
can be left intact.
+ */
+ protected static void clearOrphanRemovalRelations(OpenJPAStateManager sm,
+ FieldMetaData fmd, FieldMetaData[] orphanRemoves, Object newValue) {
+ // don't bother clearing unflushed new instances
+ if (sm.isNew() && !sm.getFlushed().get(fmd.getIndex()))
+ return;
+ if (fmd.getDeclaredTypeCode() == JavaTypes.PC) {
+ Object initial = sm.fetchInitialField(fmd.getIndex());
+ clearInverseRelations(sm, initial, fmd, orphanRemoves);
+ } else {
+ Object obj = sm.fetchInitialField(fmd.getIndex());
+ Collection initial = null;
+ if (obj instanceof Collection)
+ initial = (Collection) obj;
+ else if (obj instanceof Map)
+ initial = ((Map)obj).values();
+
+ if (initial == null)
+ return;
+
+ // clear all relations not also in the new value
+ Collection coll = null;
+ if (newValue instanceof Collection)
+ coll = (Collection) newValue;
+ else if (newValue instanceof Map)
+ coll = ((Map)newValue).values();
+ Object elem;
+ for (Iterator itr = initial.iterator(); itr.hasNext();) {
+ elem = itr.next();
+ if (coll == null || !coll.contains(elem))
+ clearInverseRelations(sm, elem, fmd, orphanRemoves);
+ }
+ }
+ }
+
+ /**
+ * Clear all inverse the relations from val
to the instance
+ * managed by sm
.
+ */
+ protected static void clearInverseRelations(OpenJPAStateManager sm, Object val,
+ FieldMetaData fmd, FieldMetaData[] orphanRemoves) {
+ if (val == null)
+ return;
+ OpenJPAStateManager other = sm.getContext().getStateManager(val);
+ if (other == null || other.isDeleted())
+ return;
+
+ for (int i = 0; i < orphanRemoves.length; i++) {
+
+ // if this is the owned side of the relation and has not yet been
+ // loaded, no point in setting it now, cause it'll have the correct
+ // value the next time it is loaded after the flush
+ switch (orphanRemoves[i].getDeclaredTypeCode()) {
+ case JavaTypes.PC:
+ if (fmd.getOrphanRemoval() || fmd.getElement().getOrphanRemoval())
+ ((StateManagerImpl)other).delete();
+ break;
+ case JavaTypes.COLLECTION:
+ removeFromCollection(other, orphanRemoves[i],
+ sm.getManagedInstance());
+ break;
+ }
+ }
+ }
+
+ /**
+ * Remove the given instance from the collection.
+ */
+ protected static void removeFromCollection(OpenJPAStateManager sm,
+ FieldMetaData fmd, Object val) {
+ Collection coll = (Collection) sm.fetchObjectField(fmd.getIndex());
+ if (coll != null)
+ coll.remove(val);
+ }
+
+
+}
diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/SaveFieldManager.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/SaveFieldManager.java
index 9a8b0f45b..0ab30f1a2 100644
--- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/SaveFieldManager.java
+++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/SaveFieldManager.java
@@ -113,11 +113,12 @@ public class SaveFieldManager
mutable = true;
}
- // if this is not an inverse field and the proper restore flag is
- // not set, skip it
+ // if this is not an inverse field or a field subject to orphanRemoval
+ // and the proper restore flag is not set, skip it
- if (_sm.getBroker().getInverseManager() == null
- || fmd.getInverseMetaDatas().length == 0) {
+ if ((_sm.getBroker().getInverseManager() == null
+ || fmd.getInverseMetaDatas().length == 0) &&
+ (fmd.getOrphanRemovalMetaDatas().length == 0)) {
// use sm's restore directive, not broker's
int restore = _sm.getBroker().getRestoreState();
if (restore == RestoreState.RESTORE_NONE
diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/SingleFieldManager.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/SingleFieldManager.java
index b2f4e4071..7fa0bfed2 100644
--- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/SingleFieldManager.java
+++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/SingleFieldManager.java
@@ -481,6 +481,11 @@ class SingleFieldManager
InverseManager manager = _broker.getInverseManager();
if (manager != null)
manager.correctRelations(_sm, fmd, objval);
+ else {
+ FieldMetaData[] orphans = fmd.getOrphanRemovalMetaDatas();
+ if (orphans.length > 0)
+ OrphanRemovalManager.correctRelations(_sm, fmd, objval);
+ }
return ret;
}
diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StateManagerImpl.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StateManagerImpl.java
index c9e7d3d68..e57158704 100644
--- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StateManagerImpl.java
+++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StateManagerImpl.java
@@ -105,6 +105,7 @@ public class StateManagerImpl
private static final int FLAG_VERSION_CHECK = 2 << 14;
private static final int FLAG_VERSION_UPDATE = 2 << 15;
private static final int FLAG_DETACHING = 2 << 16;
+ private static final int FLAG_ORPHAN_REMOVAL = 2 << 17;
private static final Localizer _loc = Localizer.forPackage
(StateManagerImpl.class);
@@ -306,6 +307,10 @@ public class StateManagerImpl
if (_broker.getInverseManager() != null
&& fmds[i].getInverseMetaDatas().length > 0)
_flags |= FLAG_INVERSES;
+ // record whether there are any managed fields subject to
+ // orphan removal
+ if (fmds[i].getOrphanRemovalMetaDatas().length > 0)
+ _flags |= FLAG_ORPHAN_REMOVAL;
}
pc.pcSetDetachedState(null);
@@ -510,6 +515,12 @@ public class StateManagerImpl
((OpenJPAId) oid).setManagedInstanceType(_meta.getDescribedType());
}
+ public StateManagerImpl getObjectIdOwner() {
+ StateManagerImpl sm = this;
+ while (sm.getOwner() != null)
+ sm = (StateManagerImpl) sm.getOwner();
+ return sm;
+ }
public boolean assignObjectId(boolean flush) {
lock();
try {
@@ -853,9 +864,12 @@ public class StateManagerImpl
public Object fetchInitialField(int field) {
FieldMetaData fmd = _meta.getField(field);
+ boolean hasInverse = (_flags & FLAG_INVERSES) != 0
+ && fmd.getInverseMetaDatas().length > 0;
+ boolean hasOrphanRemoval = (_flags & FLAG_ORPHAN_REMOVAL) != 0
+ && fmd.getOrphanRemovalMetaDatas().length > 0;
if (_broker.getRestoreState() == RestoreState.RESTORE_NONE
- && ((_flags & FLAG_INVERSES) == 0
- || fmd.getInverseMetaDatas().length == 0))
+ && (!hasInverse && !hasOrphanRemoval))
throw new InvalidStateException(_loc.get("restore-unset"));
switch (fmd.getDeclaredTypeCode()) {
@@ -867,8 +881,7 @@ public class StateManagerImpl
case JavaTypes.OBJECT:
// if we're not saving mutable types, throw an exception
if (_broker.getRestoreState() != RestoreState.RESTORE_ALL
- && ((_flags & FLAG_INVERSES) == 0
- || fmd.getInverseMetaDatas().length == 0))
+ && (!hasInverse && !hasOrphanRemoval))
throw new InvalidStateException(_loc.get
("mutable-restore-unset"));
}
@@ -2666,7 +2679,8 @@ public class StateManagerImpl
*/
void saveFields(boolean immediate) {
if (_broker.getRestoreState() == RestoreState.RESTORE_NONE
- && (_flags & FLAG_INVERSES) == 0)
+ && (((_flags & FLAG_INVERSES) == 0)
+ || ((_flags & FLAG_ORPHAN_REMOVAL) == 0)))
return;
_flags |= FLAG_SAVE;
@@ -2694,8 +2708,12 @@ public class StateManagerImpl
// if this is a managed inverse field, load it so we're sure to have
// the original value
- if (!_loaded.get(field) && ((_flags & FLAG_INVERSES) != 0
- && _meta.getField(field).getInverseMetaDatas().length > 0))
+ FieldMetaData fmd = _meta.getField(field);
+ boolean hasInverse = (_flags & FLAG_INVERSES) != 0
+ && fmd.getInverseMetaDatas().length > 0;
+ boolean hasOrphanRemoval = (_flags & FLAG_ORPHAN_REMOVAL) != 0
+ && fmd.getOrphanRemovalMetaDatas().length > 0;
+ if (!_loaded.get(field) && (hasInverse || hasOrphanRemoval))
loadField(field, LockLevels.LOCK_NONE, false, false);
// don't bother creating the save field manager if we're not going to
diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/meta/FieldMetaData.java b/openjpa-kernel/src/main/java/org/apache/openjpa/meta/FieldMetaData.java
index 151c53c04..dc7fcfaa2 100644
--- a/openjpa-kernel/src/main/java/org/apache/openjpa/meta/FieldMetaData.java
+++ b/openjpa-kernel/src/main/java/org/apache/openjpa/meta/FieldMetaData.java
@@ -210,6 +210,7 @@ public class FieldMetaData
// indicate if this field is used by other field as "order by" value
private boolean _usedInOrderBy = false;
private boolean _isElementCollection = false;
+ private FieldMetaData[] _orphanRemoves = null;
/**
* Constructor.
@@ -911,6 +912,7 @@ public class FieldMetaData
break;
case JavaTypes.ARRAY:
case JavaTypes.COLLECTION:
+ case JavaTypes.MAP:
meta = _elem.getTypeMetaData();
break;
}
@@ -979,6 +981,72 @@ public class FieldMetaData
return _inverses;
}
+ /**
+ * Return all orphanRemoval meta data of this field.
+ */
+ public FieldMetaData[] getOrphanRemovalMetaDatas() {
+ if (_orphanRemoves == null) {
+ // get the metadata for the type on the other side of this relation
+ ClassMetaData meta = null;
+ switch (getTypeCode()) {
+ case JavaTypes.PC:
+ meta = getTypeMetaData();
+ break;
+ case JavaTypes.ARRAY:
+ case JavaTypes.COLLECTION:
+ case JavaTypes.MAP:
+ meta = _elem.getTypeMetaData();
+ break;
+ }
+ Collection orphanRemoves = null;
+ if (meta != null) {
+ // scan rel type for fields that name this field as an inverse
+ FieldMetaData[] fields = meta.getFields();
+ Class type = getOwnerType();
+ //Class type = getDeclaringMetaData().getDescribedType();
+ for (int i = 0; i < fields.length; i++) {
+ // skip fields that aren't compatible with our owning class
+ switch (fields[i].getTypeCode()) {
+ case JavaTypes.PC:
+ if (!type.isAssignableFrom(fields[i].getType()))
+ continue;
+ break;
+ case JavaTypes.COLLECTION:
+ case JavaTypes.ARRAY:
+ if (!type.isAssignableFrom(fields[i].
+ getElement().getType()))
+ continue;
+ break;
+ default:
+ continue;
+ }
+
+ if (orphanRemoves == null)
+ orphanRemoves = new ArrayList(3);
+ if (!orphanRemoves.contains(fields[i]))
+ orphanRemoves.add(fields[i]);
+ }
+ }
+
+ MetaDataRepository repos = getRepository();
+ if (orphanRemoves == null)
+ _orphanRemoves = repos.EMPTY_FIELDS;
+ else
+ _orphanRemoves = (FieldMetaData[]) orphanRemoves.toArray
+ (repos.newFieldMetaDataArray(orphanRemoves.size()));
+ }
+ return _orphanRemoves;
+ }
+
+ public Class getOwnerType() {
+ ClassMetaData owner = getDefiningMetaData();
+ ValueMetaData vm = owner.getEmbeddingMetaData();
+ if (vm == null)
+ return owner.getDescribedType();
+ FieldMetaData fmd = vm.getFieldMetaData();
+ return fmd.getOwnerType();
+ }
+
/**
* The strategy to use for insert value generation.
* One of the constants from {@link ValueStrategies}.
@@ -1997,6 +2065,14 @@ public class FieldMetaData
public void setCascadeDelete(int delete) {
_val.setCascadeDelete(delete);
}
+
+ public boolean getOrphanRemoval() {
+ return _val.getOrphanRemoval();
+ }
+
+ public void setOrphanRemoval(boolean orphanRemoval) {
+ _val.setOrphanRemoval(orphanRemoval);
+ }
public int getCascadePersist() {
return _val.getCascadePersist();
diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/meta/ValueMetaData.java b/openjpa-kernel/src/main/java/org/apache/openjpa/meta/ValueMetaData.java
index ffe7ef46a..4d2f53123 100644
--- a/openjpa-kernel/src/main/java/org/apache/openjpa/meta/ValueMetaData.java
+++ b/openjpa-kernel/src/main/java/org/apache/openjpa/meta/ValueMetaData.java
@@ -302,4 +302,14 @@ public interface ValueMetaData
* information.
*/
public void copy(ValueMetaData vmd);
+
+ /**
+ * Whether this value is subject to orphanRemoval.
+ */
+ public void setOrphanRemoval(boolean orphanRemoval);
+
+ /**
+ * Whether this value is subject to orphanRemoval.
+ */
+ public boolean getOrphanRemoval();
}
diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/meta/ValueMetaDataImpl.java b/openjpa-kernel/src/main/java/org/apache/openjpa/meta/ValueMetaDataImpl.java
index ff289c8e3..1505662c6 100644
--- a/openjpa-kernel/src/main/java/org/apache/openjpa/meta/ValueMetaDataImpl.java
+++ b/openjpa-kernel/src/main/java/org/apache/openjpa/meta/ValueMetaDataImpl.java
@@ -57,6 +57,7 @@ public class ValueMetaDataImpl
private int _resMode = MODE_NONE;
private String _mappedBy = null;
private FieldMetaData _mappedByMeta = null;
+ private boolean _orphanRemoval = false;
protected ValueMetaDataImpl(FieldMetaData owner) {
_owner = owner;
@@ -227,6 +228,14 @@ public class ValueMetaDataImpl
_delete = delete;
}
+ public boolean getOrphanRemoval() {
+ return _orphanRemoval;
+ }
+
+ public void setOrphanRemoval(boolean orphanRemoval) {
+ _orphanRemoval = orphanRemoval;
+ }
+
public int getCascadePersist() {
if (_owner.getManagement() != FieldMetaData.MANAGE_PERSISTENT)
return CASCADE_NONE;
diff --git a/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/AnnotationPersistenceMappingParser.java b/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/AnnotationPersistenceMappingParser.java
index 9fd59aef7..b00badc85 100644
--- a/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/AnnotationPersistenceMappingParser.java
+++ b/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/AnnotationPersistenceMappingParser.java
@@ -1747,10 +1747,6 @@ public class AnnotationPersistenceMappingParser
* Parse @MapKeyColumn.
*/
protected void parseMapKeyColumn(FieldMapping fm, MapKeyColumn anno) {
- if (!fm.isElementCollection())
- throw new UnsupportedException(_loc.get("unsupported", fm,
- anno.toString()));
-
int unique = 0;
Column col = new Column();
setupMapKeyColumn(fm, col, anno);
diff --git a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/AnnotationPersistenceMetaDataParser.java b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/AnnotationPersistenceMetaDataParser.java
index fe2a3a99f..dbd302557 100644
--- a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/AnnotationPersistenceMetaDataParser.java
+++ b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/AnnotationPersistenceMetaDataParser.java
@@ -1330,6 +1330,7 @@ public class AnnotationPersistenceMetaDataParser
if (anno.targetEntity() != void.class)
fmd.setTypeOverride(anno.targetEntity());
setCascades(fmd, anno.cascade());
+ setOrphanRemoval(fmd, anno.orphanRemoval());
}
/**
@@ -1368,6 +1369,7 @@ public class AnnotationPersistenceMetaDataParser
if (anno.targetEntity() != void.class)
fmd.getElement().setDeclaredType(anno.targetEntity());
setCascades(fmd.getElement(), anno.cascade());
+ setOrphanRemoval(fmd.getElement(), anno.orphanRemoval());
}
/**
@@ -1563,6 +1565,11 @@ public class AnnotationPersistenceMetaDataParser
vmd.setCascadeRefresh(ValueMetaData.CASCADE_IMMEDIATE);
}
}
+ private void setOrphanRemoval(ValueMetaData vmd, boolean orphanRemoval) {
+ vmd.setOrphanRemoval(orphanRemoval);
+ if (orphanRemoval)
+ setCascades(vmd, new CascadeType[] {CascadeType.REMOVE});
+ }
/**
* Parse @SequenceGenerator.