use of org.datanucleus.exceptions.ReachableObjectNotCascadedException in project datanucleus-rdbms by datanucleus.
the class PersistableMapping method setObjectAsValue.
/**
* Method to set an object reference (FK) in the datastore.
* @param ec The ExecutionContext
* @param ps The Prepared Statement
* @param param The parameter ids in the statement
* @param value The value to put in the statement at these ids
* @param ownerOP ObjectProvider for the owner object
* @param ownerFieldNumber Field number of this PC object in the owner
* @throws NotYetFlushedException Just put "null" in and throw "NotYetFlushedException", to be caught by ParameterSetter and will signal to the
* PC object being inserted that it needs to inform this object when it is inserted.
*/
private void setObjectAsValue(ExecutionContext ec, PreparedStatement ps, int[] param, Object value, ObjectProvider ownerOP, int ownerFieldNumber) {
Object id;
ApiAdapter api = ec.getApiAdapter();
if (!api.isPersistable(value)) {
throw new NucleusException(Localiser.msg("041016", value.getClass(), value)).setFatal();
}
ObjectProvider valueOP = ec.findObjectProvider(value);
try {
ClassLoaderResolver clr = ec.getClassLoaderResolver();
// Check if the field is attributed in the datastore
boolean hasDatastoreAttributedPrimaryKeyValues = hasDatastoreAttributedPrimaryKeyValues(ec.getMetaDataManager(), storeMgr, clr);
boolean inserted = false;
if (ownerFieldNumber >= 0) {
// Field mapping : is this field of the related object present in the datastore?
inserted = storeMgr.isObjectInserted(valueOP, ownerFieldNumber);
} else if (mmd == null) {
// Identity mapping : is the object inserted far enough to be considered of this mapping type?
inserted = storeMgr.isObjectInserted(valueOP, type);
}
if (valueOP != null) {
if (ec.getApiAdapter().isDetached(value) && valueOP.getReferencedPC() != null && ownerOP != null && mmd != null) {
// Still detached but started attaching so replace the field with what will be the attached
// Note that we have "fmd != null" here hence omitting any M-N relations where this is a join table
// mapping
ownerOP.replaceFieldMakeDirty(ownerFieldNumber, valueOP.getReferencedPC());
}
if (valueOP.isWaitingToBeFlushedToDatastore()) {
try {
// Related object is not yet flushed to the datastore so flush it so we can set the FK
valueOP.flush();
} catch (NotYetFlushedException nfe) {
// Could not flush it, maybe it has a relation to this object! so set as null TODO check nullability
if (ownerOP != null) {
ownerOP.updateFieldAfterInsert(value, ownerFieldNumber);
}
setObjectAsNull(ec, ps, param);
return;
}
}
} else {
if (ec.getApiAdapter().isDetached(value)) {
// Field value is detached and not yet started attaching, so attach
Object attachedValue = ec.persistObjectInternal(value, null, -1, ObjectProvider.PC);
if (attachedValue != value && ownerOP != null) {
// Replace the field value if using copy-on-attach
ownerOP.replaceFieldMakeDirty(ownerFieldNumber, attachedValue);
// Work from attached value now that it is attached
value = attachedValue;
}
valueOP = ec.findObjectProvider(value);
}
}
// 5) the value is the same object as we are inserting anyway and has its identity set
if (inserted || !ec.isInserting(value) || (!hasDatastoreAttributedPrimaryKeyValues && (this.mmd != null && this.mmd.isPrimaryKey())) || (!hasDatastoreAttributedPrimaryKeyValues && ownerOP == valueOP && api.getIdForObject(value) != null)) {
// The PC is either already inserted, or inserted down to the level we need, or not inserted at all,
// or the field is a PK and identity not attributed by the datastore
// Object either already exists, or is not yet being inserted.
id = api.getIdForObject(value);
// Check if the persistable object exists in this datastore
boolean requiresPersisting = false;
if (ec.getApiAdapter().isDetached(value) && ownerOP != null) {
// Detached object so needs attaching
if (ownerOP.isInserting()) {
// we can just return the value now and attach later (in InsertRequest)
if (!ec.getBooleanProperty(PropertyNames.PROPERTY_ATTACH_SAME_DATASTORE)) {
if (ec.getObjectFromCache(api.getIdForObject(value)) != null) {
// Object is in cache so exists for this datastore, so no point checking
} else {
try {
Object obj = ec.findObject(api.getIdForObject(value), true, false, value.getClass().getName());
if (obj != null) {
// Make sure this object is not retained in cache etc
ObjectProvider objOP = ec.findObjectProvider(obj);
if (objOP != null) {
ec.evictFromTransaction(objOP);
}
ec.removeObjectFromLevel1Cache(api.getIdForObject(value));
}
} catch (NucleusObjectNotFoundException onfe) {
// Object doesn't yet exist
requiresPersisting = true;
}
}
}
} else {
requiresPersisting = true;
}
} else if (id == null) {
// Transient object, so we need to persist it
requiresPersisting = true;
} else {
ExecutionContext pcEC = ec.getApiAdapter().getExecutionContext(value);
if (pcEC != null && ec != pcEC) {
throw new NucleusUserException(Localiser.msg("041015"), id);
}
}
if (requiresPersisting) {
// This PC object needs persisting (new or detached) to do the "set"
if (mmd != null && !mmd.isCascadePersist() && !ec.getApiAdapter().isDetached(value)) {
// Related PC object not persistent, but cant do cascade-persist so throw exception
if (NucleusLogger.PERSISTENCE.isDebugEnabled()) {
NucleusLogger.PERSISTENCE.debug(Localiser.msg("007006", mmd.getFullFieldName()));
}
throw new ReachableObjectNotCascadedException(mmd.getFullFieldName(), value);
}
if (NucleusLogger.PERSISTENCE.isDebugEnabled()) {
NucleusLogger.PERSISTENCE.debug(Localiser.msg("007007", mmd != null ? mmd.getFullFieldName() : null));
}
try {
Object pcNew = ec.persistObjectInternal(value, null, -1, ObjectProvider.PC);
if (hasDatastoreAttributedPrimaryKeyValues) {
ec.flushInternal(false);
}
id = api.getIdForObject(pcNew);
if (ec.getApiAdapter().isDetached(value) && ownerOP != null && mmd != null) {
// Update any detached reference to refer to the attached variant
ownerOP.replaceFieldMakeDirty(ownerFieldNumber, pcNew);
RelationType relationType = mmd.getRelationType(clr);
if (relationType == RelationType.MANY_TO_ONE_BI) {
// TODO Update the container to refer to the attached object
if (NucleusLogger.PERSISTENCE.isInfoEnabled()) {
NucleusLogger.PERSISTENCE.info("PCMapping.setObject : object " + ownerOP.getInternalObjectId() + " has field " + ownerFieldNumber + " that is 1-N bidirectional." + " Have just attached the N side so should really update the reference in the 1 side collection" + " to refer to this attached object. Not yet implemented");
}
} else if (relationType == RelationType.ONE_TO_ONE_BI) {
AbstractMemberMetaData[] relatedMmds = mmd.getRelatedMemberMetaData(clr);
// TODO Cater for more than 1 related field
ObjectProvider relatedOP = ec.findObjectProvider(pcNew);
relatedOP.replaceFieldMakeDirty(relatedMmds[0].getAbsoluteFieldNumber(), ownerOP.getObject());
}
}
} catch (NotYetFlushedException e) {
setObjectAsNull(ec, ps, param);
throw new NotYetFlushedException(value);
}
}
if (valueOP != null) {
valueOP.setStoringPC();
}
// If the field doesn't map to any datastore fields (e.g remote FK), omit the set process
if (getNumberOfDatastoreMappings() > 0) {
if (IdentityUtils.isDatastoreIdentity(id)) {
Object idKey = IdentityUtils.getTargetKeyForDatastoreIdentity(id);
try {
// Try as a Long
getDatastoreMapping(0).setObject(ps, param[0], idKey);
} catch (Exception e) {
// Must be a String
getDatastoreMapping(0).setObject(ps, param[0], idKey.toString());
}
} else {
boolean fieldsSet = false;
if (IdentityUtils.isSingleFieldIdentity(id) && javaTypeMappings.length > 1) {
Object key = IdentityUtils.getTargetKeyForSingleFieldIdentity(id);
AbstractClassMetaData keyCmd = ec.getMetaDataManager().getMetaDataForClass(key.getClass(), clr);
if (keyCmd != null && keyCmd.getIdentityType() == IdentityType.NONDURABLE) {
// Embedded ID - Make sure these are called starting at lowest first, in order
// We cannot just call OP.provideFields with all fields since that does last first
ObjectProvider keyOP = ec.findObjectProvider(key);
int[] fieldNums = keyCmd.getAllMemberPositions();
FieldManager fm = new AppIDObjectIdFieldManager(param, ec, ps, javaTypeMappings);
for (int i = 0; i < fieldNums.length; i++) {
keyOP.provideFields(new int[] { fieldNums[i] }, fm);
}
fieldsSet = true;
}
}
if (!fieldsSet) {
// Copy PK fields from identity to the object
FieldManager fm = new AppIDObjectIdFieldManager(param, ec, ps, javaTypeMappings);
api.copyKeyFieldsFromIdToObject(value, new AppIdObjectIdFieldConsumer(api, fm), id);
}
}
}
} else {
if (valueOP != null) {
valueOP.setStoringPC();
}
if (getNumberOfDatastoreMappings() > 0) {
// Object is in the process of being inserted so we cant use its id currently and we need to store
// a foreign key to it (which we cant yet do). Just put "null" in and throw "NotYetFlushedException",
// to be caught by ParameterSetter and will signal to the PC object being inserted that it needs
// to inform this object when it is inserted.
setObjectAsNull(ec, ps, param);
throw new NotYetFlushedException(value);
}
}
} finally {
if (valueOP != null) {
valueOP.unsetStoringPC();
}
}
}
use of org.datanucleus.exceptions.ReachableObjectNotCascadedException in project datanucleus-rdbms by datanucleus.
the class CollectionMapping method postInsert.
/**
* Method to be called after the insert of the owner class element.
* @param ownerOP ObjectProvider of the owner
*/
public void postInsert(ObjectProvider ownerOP) {
ExecutionContext ec = ownerOP.getExecutionContext();
Collection value = (Collection) ownerOP.provideField(getAbsoluteFieldNumber());
if (containerIsStoredInSingleColumn()) {
if (value != null) {
if (mmd.getCollection().elementIsPersistent()) {
// Make sure all persistable elements have ObjectProviders
Object[] collElements = value.toArray();
for (Object elem : collElements) {
if (elem != null) {
ObjectProvider elemOP = ec.findObjectProvider(elem);
if (elemOP == null || ec.getApiAdapter().getExecutionContext(elem) == null) {
elemOP = ec.getNucleusContext().getObjectProviderFactory().newForEmbedded(ec, elem, false, ownerOP, mmd.getAbsoluteFieldNumber());
}
}
}
}
}
return;
}
if (value == null) {
// replace null collections with an empty SCO wrapper
replaceFieldWithWrapper(ownerOP, null);
return;
}
Object[] collElements = value.toArray();
if (!mmd.isCascadePersist()) {
// Check that all elements are persistent before continuing and throw exception if necessary
if (NucleusLogger.PERSISTENCE.isDebugEnabled()) {
NucleusLogger.PERSISTENCE.debug(Localiser.msg("007006", mmd.getFullFieldName()));
}
for (Object collElement : collElements) {
if (!ec.getApiAdapter().isDetached(collElement) && !ec.getApiAdapter().isPersistent(collElement)) {
// Element is not persistent so throw exception
throw new ReachableObjectNotCascadedException(mmd.getFullFieldName(), collElement);
}
}
} else {
// Reachability
if (NucleusLogger.PERSISTENCE.isDebugEnabled()) {
NucleusLogger.PERSISTENCE.debug(Localiser.msg("007007", mmd.getFullFieldName()));
}
}
// Check if some elements need attaching
// TODO Investigate if we can just use the attachCopy route below and skip off this check
boolean needsAttaching = false;
for (Object collElement : collElements) {
if (ownerOP.getExecutionContext().getApiAdapter().isDetached(collElement)) {
needsAttaching = true;
break;
}
}
if (needsAttaching) {
// Create a wrapper and attach the elements (and add the others)
SCO collWrapper = replaceFieldWithWrapper(ownerOP, null);
if (value.size() > 0) {
collWrapper.attachCopy(value);
// The attach will have put entries in the operationQueue if using optimistic, so flush them
ownerOP.getExecutionContext().flushOperationsForBackingStore(((BackedSCO) collWrapper).getBackingStore(), ownerOP);
}
} else {
if (value.size() > 0) {
// Add the elements direct to the datastore
((CollectionStore) storeMgr.getBackingStoreForField(ownerOP.getExecutionContext().getClassLoaderResolver(), mmd, value.getClass())).addAll(ownerOP, value, 0);
// Create a SCO wrapper with the elements loaded
replaceFieldWithWrapper(ownerOP, value);
} else {
if (mmd.getRelationType(ownerOP.getExecutionContext().getClassLoaderResolver()) == RelationType.MANY_TO_MANY_BI) {
// Create a SCO wrapper, pass in null so it loads any from the datastore (on other side?)
replaceFieldWithWrapper(ownerOP, null);
} else {
// Create a SCO wrapper, pass in empty collection to avoid loading from DB (extra SQL)
replaceFieldWithWrapper(ownerOP, value);
}
}
}
}
use of org.datanucleus.exceptions.ReachableObjectNotCascadedException in project datanucleus-rdbms by datanucleus.
the class ArrayMapping method postInsert.
/**
* Method to be called after the insert of the owner class element.
* @param ownerOP ObjectProvider of the owner
*/
public void postInsert(ObjectProvider ownerOP) {
ExecutionContext ec = ownerOP.getExecutionContext();
Object value = ownerOP.provideField(getAbsoluteFieldNumber());
if (value == null) {
return;
}
if (containerIsStoredInSingleColumn()) {
if (mmd.getArray().elementIsPersistent()) {
// Make sure all persistable elements have ObjectProviders
Object[] arrElements = (Object[]) value;
for (Object elem : arrElements) {
if (elem != null) {
ObjectProvider elemOP = ec.findObjectProvider(elem);
if (elemOP == null || ec.getApiAdapter().getExecutionContext(elem) == null) {
elemOP = ec.getNucleusContext().getObjectProviderFactory().newForEmbedded(ec, elem, false, ownerOP, mmd.getAbsoluteFieldNumber());
}
}
}
}
return;
}
int arrayLength = Array.getLength(value);
boolean persistentElements = (mmd.getRelationType(ec.getClassLoaderResolver()) != RelationType.NONE);
boolean needsAttaching = false;
if (persistentElements) {
Object[] array = (Object[]) value;
if (!mmd.isCascadePersist()) {
// Check that all elements are persistent before continuing and throw exception if necessary
if (NucleusLogger.PERSISTENCE.isDebugEnabled()) {
NucleusLogger.PERSISTENCE.debug(Localiser.msg("007006", mmd.getFullFieldName()));
}
for (int i = 0; i < arrayLength; i++) {
if (!ec.getApiAdapter().isDetached(array[i]) && !ec.getApiAdapter().isPersistent(array[i])) {
// Element is not persistent so throw exception
throw new ReachableObjectNotCascadedException(mmd.getFullFieldName(), array[i]);
}
}
} else {
// Reachability
if (NucleusLogger.PERSISTENCE.isDebugEnabled()) {
NucleusLogger.PERSISTENCE.debug(Localiser.msg("007007", mmd.getFullFieldName()));
}
}
for (int i = 0; i < arrayLength; i++) {
if (ownerOP.getExecutionContext().getApiAdapter().isDetached(array[i])) {
needsAttaching = true;
break;
}
}
}
if (needsAttaching) {
// Create a wrapper and attach the elements (and add the others)
SCO collWrapper = replaceFieldWithWrapper(ownerOP, null);
if (arrayLength > 0) {
collWrapper.attachCopy(value);
// The attach will have put entries in the operationQueue if using optimistic, so flush them
ownerOP.getExecutionContext().flushOperationsForBackingStore(((BackedSCO) collWrapper).getBackingStore(), ownerOP);
}
} else {
if (arrayLength > 0) {
// Add the elements direct to the datastore
((ArrayStore) storeMgr.getBackingStoreForField(ownerOP.getExecutionContext().getClassLoaderResolver(), mmd, null)).set(ownerOP, value);
}
}
}
use of org.datanucleus.exceptions.ReachableObjectNotCascadedException in project datanucleus-rdbms by datanucleus.
the class MapMapping method postInsert.
/**
* Method to be called after the insert of the owner class element.
* @param ownerOP ObjectProvider of the owner
*/
public void postInsert(ObjectProvider ownerOP) {
ExecutionContext ec = ownerOP.getExecutionContext();
java.util.Map value = (java.util.Map) ownerOP.provideField(getAbsoluteFieldNumber());
if (containerIsStoredInSingleColumn()) {
// Do nothing when serialised since we are handled in the main request
if (value != null) {
if (mmd.getMap().keyIsPersistent() || mmd.getMap().valueIsPersistent()) {
// Make sure all persistable keys/values have ObjectProviders
Set entries = value.entrySet();
Iterator iter = entries.iterator();
while (iter.hasNext()) {
Map.Entry entry = (Map.Entry) iter.next();
if (mmd.getMap().keyIsPersistent() && entry.getKey() != null) {
Object key = entry.getKey();
if (ec.findObjectProvider(key) == null || ec.getApiAdapter().getExecutionContext(key) == null) {
ec.getNucleusContext().getObjectProviderFactory().newForEmbedded(ec, key, false, ownerOP, mmd.getAbsoluteFieldNumber());
}
}
if (mmd.getMap().valueIsPersistent() && entry.getValue() != null) {
Object val = entry.getValue();
if (ec.findObjectProvider(val) == null || ec.getApiAdapter().getExecutionContext(val) == null) {
ec.getNucleusContext().getObjectProviderFactory().newForEmbedded(ec, val, false, ownerOP, mmd.getAbsoluteFieldNumber());
}
}
}
}
}
return;
}
if (value == null) {
// replace null map with an empty SCO wrapper
replaceFieldWithWrapper(ownerOP, null);
return;
}
if (!mmd.isCascadePersist()) {
// Check that all keys/values are persistent before continuing and throw exception if necessary
if (NucleusLogger.PERSISTENCE.isDebugEnabled()) {
NucleusLogger.PERSISTENCE.debug(Localiser.msg("007006", mmd.getFullFieldName()));
}
ApiAdapter api = ec.getApiAdapter();
Set entries = value.entrySet();
Iterator iter = entries.iterator();
while (iter.hasNext()) {
Map.Entry entry = (Map.Entry) iter.next();
if (api.isPersistable(entry.getKey())) {
if (!api.isPersistent(entry.getKey()) && !api.isDetached(entry.getKey())) {
// Key is not persistent so throw exception
throw new ReachableObjectNotCascadedException(mmd.getFullFieldName(), entry.getKey());
}
}
if (api.isPersistable(entry.getValue())) {
if (!api.isPersistent(entry.getValue()) && !api.isDetached(entry.getValue())) {
// Value is not persistent so throw exception
throw new ReachableObjectNotCascadedException(mmd.getFullFieldName(), entry.getValue());
}
}
}
} else {
// Reachability
if (NucleusLogger.PERSISTENCE.isDebugEnabled()) {
NucleusLogger.PERSISTENCE.debug(Localiser.msg("007007", mmd.getFullFieldName()));
}
}
if (value.size() > 0) {
// Add the entries direct to the datastore
((MapStore) table.getStoreManager().getBackingStoreForField(ownerOP.getExecutionContext().getClassLoaderResolver(), mmd, value.getClass())).putAll(ownerOP, value);
// Create a SCO wrapper with the entries loaded
replaceFieldWithWrapper(ownerOP, value);
} else {
if (mmd.getRelationType(ownerOP.getExecutionContext().getClassLoaderResolver()) == RelationType.MANY_TO_MANY_BI) {
// Create a SCO wrapper, pass in null so it loads any from the datastore (on other side?)
replaceFieldWithWrapper(ownerOP, null);
} else {
// Create a SCO wrapper, pass in empty map to avoid loading from DB (extra SQL)
replaceFieldWithWrapper(ownerOP, value);
}
}
}
Aggregations