use of org.datanucleus.state.DNStateManager 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 ownerSM StateManager 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, DNStateManager ownerSM, int ownerFieldNumber) {
Object id;
ApiAdapter api = ec.getApiAdapter();
if (!api.isPersistable(value)) {
throw new NucleusException(Localiser.msg("041016", value.getClass(), value)).setFatal();
}
DNStateManager valueSM = ec.findStateManager(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(valueSM, ownerFieldNumber);
} else if (mmd == null) {
// Identity mapping : is the object inserted far enough to be considered of this mapping type?
inserted = storeMgr.isObjectInserted(valueSM, type);
}
if (valueSM != null) {
if (ec.getApiAdapter().isDetached(value) && valueSM.getReferencedPC() != null && ownerSM != 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
ownerSM.replaceFieldMakeDirty(ownerFieldNumber, valueSM.getReferencedPC());
}
if (valueSM.isWaitingToBeFlushedToDatastore()) {
try {
// Related object is not yet flushed to the datastore so flush it so we can set the FK
valueSM.flush();
} catch (NotYetFlushedException nfe) {
// Could not flush it, maybe it has a relation to this object! so set as null TODO check nullability
if (ownerSM != null) {
ownerSM.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, PersistableObjectType.PC);
if (attachedValue != value && ownerSM != null) {
// Replace the field value if using copy-on-attach
ownerSM.replaceFieldMakeDirty(ownerFieldNumber, attachedValue);
// Work from attached value now that it is attached
value = attachedValue;
}
valueSM = ec.findStateManager(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 && ownerSM == valueSM && 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) && ownerSM != null) {
// Detached object so needs attaching
if (ownerSM.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
DNStateManager sm = ec.findStateManager(obj);
if (sm != null) {
ec.evictFromTransaction(sm);
}
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", ownerSM != null ? IdentityUtils.getPersistableIdentityForId(ownerSM.getInternalObjectId()) : id, mmd != null ? mmd.getFullFieldName() : null));
}
try {
Object pcNew = ec.persistObjectInternal(value, null, -1, PersistableObjectType.PC);
if (hasDatastoreAttributedPrimaryKeyValues) {
ec.flushInternal(false);
}
id = api.getIdForObject(pcNew);
if (ec.getApiAdapter().isDetached(value) && ownerSM != null && mmd != null) {
// Update any detached reference to refer to the attached variant
ownerSM.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 " + ownerSM.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
DNStateManager relatedSM = ec.findStateManager(pcNew);
relatedSM.replaceFieldMakeDirty(relatedMmds[0].getAbsoluteFieldNumber(), ownerSM.getObject());
}
}
} catch (NotYetFlushedException e) {
setObjectAsNull(ec, ps, param);
throw new NotYetFlushedException(value);
}
}
if (valueSM != null) {
valueSM.setStoringPC();
}
// If the field doesn't map to any columns (e.g remote FK), omit the set process
if (getNumberOfColumnMappings() > 0) {
if (IdentityUtils.isDatastoreIdentity(id)) {
Object idKey = IdentityUtils.getTargetKeyForDatastoreIdentity(id);
try {
// Try as a Long
getColumnMapping(0).setObject(ps, param[0], idKey);
} catch (Exception e) {
// Must be a String
getColumnMapping(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 SM.provideFields with all fields since that does last first
DNStateManager keySM = ec.findStateManager(key);
int[] fieldNums = keyCmd.getAllMemberPositions();
FieldManager fm = new AppIDObjectIdFieldManager(param, ec, ps, javaTypeMappings);
for (int i = 0; i < fieldNums.length; i++) {
keySM.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 (valueSM != null) {
valueSM.setStoringPC();
}
if (getNumberOfColumnMappings() > 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 (valueSM != null) {
valueSM.unsetStoringPC();
}
}
}
use of org.datanucleus.state.DNStateManager in project datanucleus-rdbms by datanucleus.
the class AbstractContainerMapping method setObject.
/**
* Method to set a field in the passed JDBC PreparedStatement using this mapping.
* Only valid when the collection is serialised.
* @param ec ExecutionContext
* @param ps The JDBC Prepared Statement to be populated
* @param exprIndex The parameter positions in the JDBC statement to populate.
* @param value The value to populate into it
*/
public void setObject(ExecutionContext ec, PreparedStatement ps, int[] exprIndex, Object value) {
if (mmd == null || !containerIsStoredInSingleColumn()) {
throw new NucleusException(failureMessage("setObject")).setFatal();
}
DNStateManager[] sms = null;
ApiAdapter api = ec.getApiAdapter();
if (value != null) {
Collection smsColl = null;
if (value instanceof java.util.Collection) {
Iterator elementsIter = ((java.util.Collection) value).iterator();
while (elementsIter.hasNext()) {
Object elem = elementsIter.next();
if (api.isPersistable(elem)) {
DNStateManager sm = ec.findStateManager(elem);
if (sm != null) {
if (smsColl == null) {
smsColl = new HashSet();
}
smsColl.add(sm);
}
}
}
} else if (value instanceof java.util.Map) {
Iterator entriesIter = ((java.util.Map) value).entrySet().iterator();
while (entriesIter.hasNext()) {
Map.Entry entry = (Map.Entry) entriesIter.next();
Object key = entry.getKey();
Object val = entry.getValue();
if (api.isPersistable(key)) {
DNStateManager sm = ec.findStateManager(key);
if (sm != null) {
if (smsColl == null) {
smsColl = new HashSet();
}
smsColl.add(sm);
}
}
if (api.isPersistable(val)) {
DNStateManager sm = ec.findStateManager(val);
if (sm != null) {
if (smsColl == null) {
smsColl = new HashSet();
}
smsColl.add(sm);
}
}
}
}
if (smsColl != null) {
sms = (DNStateManager[]) smsColl.toArray(new DNStateManager[smsColl.size()]);
}
}
if (sms != null) {
// Set all PC objects as being stored (so we dont detach them in any serialisation process)
for (int i = 0; i < sms.length; i++) {
sms[i].setStoringPC();
}
}
getColumnMapping(0).setObject(ps, exprIndex[0], value);
if (sms != null) {
// Unset all PC objects now they are stored
for (int i = 0; i < sms.length; i++) {
sms[i].unsetStoringPC();
}
}
}
use of org.datanucleus.state.DNStateManager in project datanucleus-rdbms by datanucleus.
the class CollectionMapping method postUpdate.
/**
* Method to be called after any update of the owner class element.
* This method could be called in two situations
* <ul>
* <li>Update a collection field of an object by replacing the collection with a new collection, so UpdateRequest is called, which calls here</li>
* <li>Persist a new object, and it needed to wait til the element was inserted so goes into dirty state and then flush() triggers UpdateRequest, which comes here</li>
* </ul>
* @param ownerSM StateManager of the owner
*/
public void postUpdate(DNStateManager ownerSM) {
ExecutionContext ec = ownerSM.getExecutionContext();
Collection value = (Collection) ownerSM.provideField(getAbsoluteFieldNumber());
if (containerIsStoredInSingleColumn()) {
if (value != null) {
if (mmd.getCollection().elementIsPersistent()) {
// Make sure all persistable elements have StateManagers
Object[] collElements = value.toArray();
for (Object collElement : collElements) {
if (collElement != null) {
DNStateManager elemSM = ec.findStateManager(collElement);
if (elemSM == null || ec.getApiAdapter().getExecutionContext(collElement) == null) {
elemSM = ec.getNucleusContext().getStateManagerFactory().newForEmbedded(ec, collElement, false, ownerSM, mmd.getAbsoluteFieldNumber(), PersistableObjectType.EMBEDDED_COLLECTION_ELEMENT_PC);
}
}
}
}
}
return;
}
if (value == null) {
// remove any elements in the collection and replace it with an empty SCO wrapper
((CollectionStore) storeMgr.getBackingStoreForField(ec.getClassLoaderResolver(), mmd, null)).clear(ownerSM);
replaceFieldWithWrapper(ownerSM, null);
return;
}
if (value instanceof BackedSCO) {
// Already have a SCO value, so flush outstanding updates
ec.flushOperationsForBackingStore(((BackedSCO) value).getBackingStore(), ownerSM);
return;
}
if (mmd.isCascadePersist()) {
if (NucleusLogger.PERSISTENCE.isDebugEnabled()) {
NucleusLogger.PERSISTENCE.debug(Localiser.msg("007009", IdentityUtils.getPersistableIdentityForId(ownerSM.getInternalObjectId()), mmd.getFullFieldName()));
}
CollectionStore backingStore = ((CollectionStore) storeMgr.getBackingStoreForField(ec.getClassLoaderResolver(), mmd, value.getClass()));
backingStore.update(ownerSM, value);
// Replace the field with a wrapper containing these elements
replaceFieldWithWrapper(ownerSM, value);
} else {
// User doesn't want to update by reachability
if (NucleusLogger.PERSISTENCE.isDebugEnabled()) {
NucleusLogger.PERSISTENCE.debug(Localiser.msg("007008", IdentityUtils.getPersistableIdentityForId(ownerSM.getInternalObjectId()), mmd.getFullFieldName()));
}
return;
}
}
use of org.datanucleus.state.DNStateManager in project datanucleus-rdbms by datanucleus.
the class CollectionMapping method postInsert.
// --------------- Implementation of MappingCallbacks -------------------
/**
* Method to be called after the insert of the owner class element.
* @param ownerSM StateManager of the owner
*/
public void postInsert(DNStateManager ownerSM) {
ExecutionContext ec = ownerSM.getExecutionContext();
Collection value = (Collection) ownerSM.provideField(getAbsoluteFieldNumber());
if (containerIsStoredInSingleColumn()) {
if (value != null) {
if (mmd.getCollection().elementIsPersistent()) {
// Make sure all persistable elements have StateManagers
Object[] collElements = value.toArray();
for (Object elem : collElements) {
if (elem != null) {
DNStateManager elemSM = ec.findStateManager(elem);
if (elemSM == null || ec.getApiAdapter().getExecutionContext(elem) == null) {
elemSM = ec.getNucleusContext().getStateManagerFactory().newForEmbedded(ec, elem, false, ownerSM, mmd.getAbsoluteFieldNumber(), PersistableObjectType.EMBEDDED_COLLECTION_ELEMENT_PC);
}
}
}
}
}
return;
}
if (value == null) {
// replace null collections with an empty SCO wrapper
replaceFieldWithWrapper(ownerSM, 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", IdentityUtils.getPersistableIdentityForId(ownerSM.getInternalObjectId()), 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 (ec.getApiAdapter().isDetached(collElement)) {
needsAttaching = true;
break;
}
}
if (needsAttaching) {
// Create a wrapper and attach the elements (and add the others)
SCO collWrapper = replaceFieldWithWrapper(ownerSM, null);
if (value.size() > 0) {
collWrapper.attachCopy(value);
// The attach will have put entries in the operationQueue if using optimistic, so flush them
ec.flushOperationsForBackingStore(((BackedSCO) collWrapper).getBackingStore(), ownerSM);
}
} else {
if (value.size() > 0) {
// Add the elements direct to the datastore
((CollectionStore) storeMgr.getBackingStoreForField(ec.getClassLoaderResolver(), mmd, value.getClass())).addAll(ownerSM, value, 0);
// Create a SCO wrapper with the elements loaded
replaceFieldWithWrapper(ownerSM, value);
} else {
if (mmd.getRelationType(ec.getClassLoaderResolver()) == RelationType.MANY_TO_MANY_BI) {
// Create a SCO wrapper, pass in null so it loads any from the datastore (on other side?)
replaceFieldWithWrapper(ownerSM, null);
} else {
// Create a SCO wrapper, pass in empty collection to avoid loading from DB (extra SQL)
replaceFieldWithWrapper(ownerSM, value);
}
}
}
}
use of org.datanucleus.state.DNStateManager in project datanucleus-rdbms by datanucleus.
the class EmbeddedMapping method getObject.
/**
* Accessor for the embedded object from the result set
* @param ec ExecutionContext
* @param rs The ResultSet
* @param ownerSM StateManager of the owning object containing this embedded object
* @param ownerFieldNumber Field number in the owning object where this is stored
* @param param Array of param numbers in the ResultSet for the fields of this object
* @return The embedded object
*/
@Override
public Object getObject(ExecutionContext ec, ResultSet rs, int[] param, DNStateManager ownerSM, int ownerFieldNumber) {
Object value = null;
// Determine the type of the embedded object
AbstractClassMetaData embCmd = this.embCmd;
int n = 0;
if (discrimMapping != null) {
Object discrimValue = discrimMapping.getObject(ec, rs, new int[] { param[n] });
String className = ec.getMetaDataManager().getClassNameFromDiscriminatorValue((String) discrimValue, discrimMetaData);
embCmd = storeMgr.getMetaDataManager().getMetaDataForClass(className, clr);
n++;
}
// Create a persistable to put the values into
DNStateManager embSM = ec.getNucleusContext().getStateManagerFactory().newForEmbedded(ec, embCmd, ownerSM, mmd.getAbsoluteFieldNumber(), objectType);
value = embSM.getObject();
String nullColumn = (emd != null) ? emd.getNullIndicatorColumn() : null;
String nullValue = (emd != null) ? emd.getNullIndicatorValue() : null;
// Populate the field values
int numJavaMappings = javaTypeMappings.size();
for (int i = 0; i < numJavaMappings; i++) {
JavaTypeMapping mapping = javaTypeMappings.get(i);
int embAbsFieldNum = embCmd.getAbsolutePositionOfMember(mapping.getMemberMetaData().getName());
if (embAbsFieldNum >= 0) {
// Mapping for field that is present in this embedded type, so set the field
if (mapping instanceof EmbeddedPCMapping) {
// We have a nested embedded
int numSubParams = mapping.getNumberOfColumnMappings();
int[] subParam = new int[numSubParams];
int k = 0;
for (int j = n; j < n + numSubParams; j++) {
subParam[k++] = param[j];
}
n += numSubParams;
// Use the sub-object mapping to extract the value for that object
Object subValue = mapping.getObject(ec, rs, subParam, embSM, embAbsFieldNum);
if (subValue != null) {
embSM.replaceField(embAbsFieldNum, subValue);
}
// TODO Check the null column and its value in the sub-embedded ?
} else {
if (mapping.getNumberOfColumnMappings() > 0) {
// Extract the value(s) for this field and update the PC if it is not null
int[] posMapping = new int[mapping.getNumberOfColumnMappings()];
for (int j = 0; j < posMapping.length; j++) {
posMapping[j] = param[n++];
}
Object fieldValue = mapping.getObject(ec, rs, posMapping);
// Check for the null column and its value and break if this matches the null check
if (nullColumn != null && mapping.getMemberMetaData().getColumnMetaData()[0].getName().equals(nullColumn)) {
if ((nullValue == null && fieldValue == null) || (nullValue != null && fieldValue != null && fieldValue.toString().equals(nullValue))) {
value = null;
break;
}
}
// Set the field value
if (fieldValue != null) {
embSM.replaceField(embAbsFieldNum, fieldValue);
} else {
// If the value is null, but the field is not a primitive update it
AbstractMemberMetaData embFmd = embCmd.getMetaDataForManagedMemberAtAbsolutePosition(embAbsFieldNum);
if (!embFmd.getType().isPrimitive()) {
embSM.replaceField(embAbsFieldNum, fieldValue);
}
}
}
}
} else {
// Mapping not present in this embedded type so maybe subclass, so just omit the positions
int numSubParams = mapping.getNumberOfColumnMappings();
n += numSubParams;
}
}
// Update owner field in the element (if present)
if (emd != null) {
String ownerField = emd.getOwnerMember();
if (ownerField != null) {
int ownerFieldNumberInElement = embCmd.getAbsolutePositionOfMember(ownerField);
if (ownerFieldNumberInElement >= 0) {
embSM.replaceField(ownerFieldNumberInElement, ownerSM != null ? ownerSM.getObject() : null);
}
}
}
return value;
}
Aggregations