mirror of https://github.com/apache/openjpa.git
OPENJPA-870: orphanRemoval support
git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@739155 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
e47d83b051
commit
6453c64343
|
@ -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 &&
|
||||
|
|
|
@ -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 <code>fromRef</code> to <code>toRef</code>; 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()
|
||||
|
|
|
@ -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 <code>fmd</code> of the instance managed by <code>sm</code> has
|
||||
* value <code>value</code>. Ensure that all inverses relations from
|
||||
* <code>value</code> 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 <code>fmd</code> for
|
||||
* the instance managed by <code>sm</code> and its inverses. Relations
|
||||
* shared with <code>newValue</code> 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 <code>val</code> to the instance
|
||||
* managed by <code>sm</code>.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in New Issue