use of org.datanucleus.enhancement.Persistable in project datanucleus-core by datanucleus.
the class StateManagerImpl method detach.
/**
* Method to detach this object.
* If the object is detachable then it will be migrated to DETACHED state, otherwise will migrate to TRANSIENT. Used by "DetachAllOnCommit"/"DetachAllOnRollback"
* @param state State for the detachment process
*/
public void detach(FetchPlanState state) {
if (myEC == null) {
return;
}
ApiAdapter api = myEC.getApiAdapter();
if (myLC.isDeleted() || api.isDetached(myPC) || isDetaching()) {
// Already deleted, detached or being detached
return;
}
// Check if detachable ... if so then we detach a copy, otherwise we return a transient copy
boolean detachable = api.isDetachable(myPC);
if (detachable) {
if (NucleusLogger.PERSISTENCE.isDebugEnabled()) {
NucleusLogger.PERSISTENCE.debug(Localiser.msg("010009", StringUtils.toJVMIDString(myPC), "" + state.getCurrentFetchDepth()));
}
// Call any "pre-detach" listeners
getCallbackHandler().preDetach(myPC);
}
try {
setDetaching(true);
String detachedState = myEC.getNucleusContext().getConfiguration().getStringProperty(PropertyNames.PROPERTY_DETACH_DETACHED_STATE);
if (detachedState.equalsIgnoreCase("all")) {
loadUnloadedFields();
} else if (detachedState.equalsIgnoreCase("loaded")) {
// Do nothing since just using currently loaded fields
} else {
// Using fetch-groups, so honour detachmentOptions for loading/unloading
if ((myEC.getFetchPlan().getDetachmentOptions() & FetchPlan.DETACH_LOAD_FIELDS) != 0) {
// Load any unloaded fetch-plan fields
loadUnloadedFieldsInFetchPlan();
}
if ((myEC.getFetchPlan().getDetachmentOptions() & FetchPlan.DETACH_UNLOAD_FIELDS) != 0) {
// Unload any loaded fetch-plan fields that aren't in the current fetch plan
unloadNonFetchPlanFields();
// Remove the values from the detached object - not required by the spec
int[] unloadedFields = ClassUtils.getFlagsSetTo(loadedFields, cmd.getAllMemberPositions(), false);
if (unloadedFields != null && unloadedFields.length > 0) {
Persistable dummyPC = myPC.dnNewInstance(this);
myPC.dnCopyFields(dummyPC, unloadedFields);
replaceStateManager(dummyPC, null);
}
}
}
// Detach all (loaded) fields in the FetchPlan
FieldManager detachFieldManager = new DetachFieldManager(this, cmd.getSCOMutableMemberFlags(), myFP, state, false);
for (int i = 0; i < loadedFields.length; i++) {
if (loadedFields[i]) {
try {
// Just fetch the field since we are usually called in postCommit() so dont want to update it
detachFieldManager.fetchObjectField(i);
} catch (EndOfFetchPlanGraphException eofpge) {
Object value = provideField(i);
if (api.isPersistable(value)) {
// PC field beyond end of graph
ObjectProvider valueOP = myEC.findObjectProvider(value);
if (!api.isDetached(value) && !(valueOP != null && valueOP.isDetaching())) {
// Field value is not detached or being detached so unload it
String fieldName = cmd.getMetaDataForManagedMemberAtAbsolutePosition(i).getName();
if (NucleusLogger.PERSISTENCE.isDebugEnabled()) {
NucleusLogger.PERSISTENCE.debug(Localiser.msg("026032", StringUtils.toJVMIDString(myPC), IdentityUtils.getPersistableIdentityForId(myID), fieldName));
}
unloadField(fieldName);
}
}
// TODO What if we have collection/map that includes some objects that are not detached?
// Currently we just leave as persistent etc but should we????
// The problem is that with 1-N bidir fields we could unload the field incorrectly
}
}
}
if (detachable) {
// Migrate the lifecycle state to DETACHED_CLEAN
myLC = myLC.transitionDetach(this);
// Update the object with its detached state
myPC.dnReplaceFlags();
((Detachable) myPC).dnReplaceDetachedState();
// Call any "post-detach" listeners
// there is no copy, so give the same object
getCallbackHandler().postDetach(myPC, myPC);
Persistable toCheckPC = myPC;
Object toCheckID = myID;
disconnect();
if (!toCheckPC.dnIsDetached()) {
// Sanity check on the objects detached state
throw new NucleusUserException(Localiser.msg("026025", toCheckPC.getClass().getName(), toCheckID));
}
} else {
// Warn the user since they selected detachAllOnCommit
NucleusLogger.PERSISTENCE.warn(Localiser.msg("026031", myPC.getClass().getName(), IdentityUtils.getPersistableIdentityForId(myID)));
// Make the object transient
makeTransient(null);
}
} finally {
setDetaching(false);
}
}
use of org.datanucleus.enhancement.Persistable in project datanucleus-core by datanucleus.
the class StateManagerImpl method refreshFieldsInFetchPlan.
/**
* Refreshes from the database all fields in fetch plan.
* Called by life-cycle transitions when the object undergoes a "transitionRefresh".
*/
public void refreshFieldsInFetchPlan() {
int[] fieldNumbers = myFP.getMemberNumbers();
if (fieldNumbers != null && fieldNumbers.length > 0) {
clearDirtyFlags(fieldNumbers);
ClassUtils.clearFlags(loadedFields, fieldNumbers);
// Can't refresh PK fields!
markPKFieldsAsLoaded();
boolean callPostLoad = myFP.isToCallPostLoadFetchPlan(this.loadedFields);
// Refresh the fetch plan fields in this object
// Make sure that the version is reset upon fetch
setTransactionalVersion(null);
loadFieldsFromDatastore(fieldNumbers);
if (cmd.hasRelations(myEC.getClassLoaderResolver())) {
// Check for cascade refreshes to related objects
for (int i = 0; i < fieldNumbers.length; i++) {
AbstractMemberMetaData fmd = cmd.getMetaDataForManagedMemberAtAbsolutePosition(fieldNumbers[i]);
RelationType relationType = fmd.getRelationType(myEC.getClassLoaderResolver());
if (relationType != RelationType.NONE && fmd.isCascadeRefresh()) {
// Need to refresh the related field object(s)
Object value = provideField(fieldNumbers[i]);
if (value != null) {
if (fmd.hasContainer()) {
// TODO This should replace the SCO wrapper with a new one, or reload the wrapper
ApiAdapter api = getExecutionContext().getApiAdapter();
ContainerHandler containerHandler = myEC.getTypeManager().getContainerHandler(fmd.getType());
for (Object object : containerHandler.getAdapter(value)) {
if (api.isPersistable(object)) {
getExecutionContext().refreshObject(object);
}
}
} else if (value instanceof Persistable) {
// Refresh any PC fields
myEC.refreshObject(value);
}
}
}
}
}
updateLevel2CacheForFields(fieldNumbers);
if (callPostLoad) {
postLoad();
}
getCallbackHandler().postRefresh(myPC);
}
}
use of org.datanucleus.enhancement.Persistable in project datanucleus-core by datanucleus.
the class StateManagerImpl method updateLevel2CacheForFields.
/**
* Convenience method to update a Level2 cached version of this object if cacheable
* and has not been modified during this transaction.
* @param fieldNumbers Numbers of fields to update in L2 cached object
*/
protected void updateLevel2CacheForFields(int[] fieldNumbers) {
String updateMode = (String) myEC.getProperty(PropertyNames.PROPERTY_CACHE_L2_UPDATE_MODE);
if (updateMode != null && updateMode.equalsIgnoreCase("commit-only")) {
return;
}
if (fieldNumbers == null || fieldNumbers.length == 0) {
return;
}
Level2Cache l2cache = myEC.getNucleusContext().getLevel2Cache();
if (l2cache != null && myEC.getNucleusContext().isClassCacheable(cmd) && !myEC.isObjectModifiedInTransaction(myID)) {
CachedPC<Persistable> cachedPC = l2cache.get(myID);
if (cachedPC != null) {
// This originally just updated the L2 cache for fields where the L2 cache didn't have a value for that field, like this
/*
int[] cacheFieldsToLoad = ClassUtils.getFlagsSetTo(cachedPC.getLoadedFields(), fieldNumbers, false);
if (cacheFieldsToLoad == null || cacheFieldsToLoad.length == 0)
{
return;
}
*/
int[] cacheFieldsToLoad = fieldNumbers;
CachedPC copyCachedPC = cachedPC.getCopy();
if (NucleusLogger.CACHE.isDebugEnabled()) {
NucleusLogger.CACHE.debug(Localiser.msg("026033", StringUtils.toJVMIDString(getObject()), myID, StringUtils.intArrayToString(cacheFieldsToLoad)));
}
provideFields(cacheFieldsToLoad, new L2CachePopulateFieldManager(this, copyCachedPC));
// Replace the current L2 cached object with this one
myEC.getNucleusContext().getLevel2Cache().put(getInternalObjectId(), copyCachedPC);
}
}
}
use of org.datanucleus.enhancement.Persistable in project datanucleus-core by datanucleus.
the class StateManagerImpl method initialiseForPersistentNew.
/**
* Initialises a state manager to manage a transient instance that is becoming newly persistent.
* A new object ID for the instance is obtained from the store manager and the object is inserted in the data store.
* <p>
* This constructor is used for assigning state managers to existing instances that are transitioning to a persistent state.
* @param pc the instance being make persistent.
* @param preInsertChanges Any changes to make before inserting
*/
public void initialiseForPersistentNew(Persistable pc, FieldValues preInsertChanges) {
myPC = pc;
myLC = myEC.getNucleusContext().getApiAdapter().getLifeCycleState(LifeCycleState.P_NEW);
persistenceFlags = Persistable.READ_OK;
for (int i = 0; i < loadedFields.length; ++i) {
loadedFields[i] = true;
}
// Assign this StateManager to the PC
replaceStateManager(myPC, this);
myPC.dnReplaceFlags();
saveFields();
// Populate all fields that have "value-strategy" and are not datastore populated
populateValueGenerationFields();
if (preInsertChanges != null) {
// Apply any pre-insert field updates
preInsertChanges.fetchFields(this);
}
if (cmd.getIdentityType() == IdentityType.APPLICATION) {
// Make sure any PK fields that are Persistable are persisted first, so we have an id to assign our identity
int[] pkFieldNumbers = cmd.getPKMemberPositions();
for (int i = 0; i < pkFieldNumbers.length; i++) {
int fieldNumber = pkFieldNumbers[i];
AbstractMemberMetaData fmd = cmd.getMetaDataForManagedMemberAtAbsolutePosition(fieldNumber);
if (myEC.getMetaDataManager().getMetaDataForClass(fmd.getType(), getExecutionContext().getClassLoaderResolver()) != null) {
try {
if (myEC.getMultithreaded()) {
myEC.getLock().lock();
lock.lock();
}
FieldManager prevFM = currFM;
try {
currFM = new SingleValueFieldManager();
myPC.dnProvideField(fieldNumber);
Persistable pkFieldPC = (Persistable) ((SingleValueFieldManager) currFM).fetchObjectField(fieldNumber);
if (pkFieldPC == null) {
throw new NucleusUserException(Localiser.msg("026016", fmd.getFullFieldName()));
}
if (!myEC.getApiAdapter().isPersistent(pkFieldPC)) {
// Make sure the PC field is persistent - can cause the insert of our object being managed by this SM via flush() when bidir relation
Object persistedFieldPC = myEC.persistObjectInternal(pkFieldPC, null, null, -1, ObjectProvider.PC);
replaceField(myPC, fieldNumber, persistedFieldPC, false);
}
} finally {
currFM = prevFM;
}
} finally {
if (myEC.getMultithreaded()) {
lock.unlock();
myEC.getLock().unlock();
}
}
}
}
}
// Set the identity of this object
setIdentity(false);
if (myEC.getTransaction().isActive()) {
myEC.enlistInTransaction(this);
}
// Now in PERSISTENT_NEW so call any callbacks/listeners
getCallbackHandler().postCreate(myPC);
if (myEC.getManageRelations()) {
// Managed Relations : register non-null bidir fields for later processing
ClassLoaderResolver clr = myEC.getClassLoaderResolver();
int[] relationPositions = cmd.getRelationMemberPositions(clr);
if (relationPositions != null) {
for (int i = 0; i < relationPositions.length; i++) {
AbstractMemberMetaData mmd = cmd.getMetaDataForManagedMemberAtAbsolutePosition(relationPositions[i]);
if (RelationType.isBidirectional(mmd.getRelationType(clr))) {
Object value = provideField(relationPositions[i]);
if (value != null) {
// Store the field with value of null so it gets checked
myEC.getRelationshipManager(this).relationChange(relationPositions[i], null, value);
}
}
}
}
}
}
Aggregations