StateManager savedImage retains references - not the original values


ebenzacar@...
 
Edited

Hi,

I've been trying to work with the StateManager and its savedImage object which I had expected to be a representation of the current object at the time that "saveFields()" is called.  Unfortunately, I just noticed that this is not the case for any Array / Collection elements.  That is to say, that the array object is copied from the source Persistable to the savedImage,  but as a shallow-copy only; the savedImage retains the same Collection/Array object as the source.  Which means that any changes to the source Collection will be reflected in the savedImage's collection as well.

To be more clear, here is some sample objects I'm working with:

Persistable:
public class Address implements Detachable, Persistable { @Join(column="ID") @Element(column="ELEMENT") Street[] street;
....
....
protected final void dnCopyField(Address obj, int index) { switch (index) { case 0: this.street = obj.street; break; default: throw new IllegalArgumentException("out of field index :" + index); } }

...

}


StateManagerImpl:

public void
saveFields()
{
savedImage = myPC.dnNewInstance(this);
savedImage.dnCopyFields(myPC, cmd.getAllMemberPositions());
savedPersistenceFlags = persistenceFlags;
savedLoadedFields = loadedFields.clone();
}


A few questions:
1) Is this intentional?
2) Is there anyway to override this behaviour, and keep a deep copy of the value instead of a referential value only?
3) Is there any other way I can capture the original value of a Collection/Array/Map/etc prior to it being updated/modified?  Are there any methods in the StateManager (or elsewhere) that can identify when the value of Collection/etc is being modified?
I read in the DN docs regarding SCOs:
> proxy : whether the field is represented by a "proxy" that intercepts any operations to detect whether it has changed internally.

Where can I find the implementation of this "proxy"?  Is it enabled automatically/by default?  Do I need to enable a flag in the dn persistence manager to enable it?

Thanks,

Eric


Andy
 
Edited

1. Talk to developers of TJDO, they wrote basic StateManager handling before DataNucleus existed.
2. If it kept a deep copy then you'd get an object graph stored alongside each object. A StateManager needs to be minimal in storage, or you slow things down. You have seen the code so can see there is no way to "override" it with current codebase, without contributions.
3. No idea.

When a SCO collection/map (not an array, since an array is not a type) field is accessed a user is returned the wrapper / proxy, which will be one of these for RDBMS, and one of these for non-RDBMS. It is enabled otherwise DataNucleus would never catch updates to the collection/map.


ebenzacar@...
 

I found the set of Proxy collections in the `org.datanucleus.store.types.wrappers`.  I don't see any state trackers in the SCO proxies that would retain the original values before they are modified.  Does anything like this exist in some other format?

I see that DN follows a very similar pattern/design to OpenJPA (and likely Hibernate/etc) with its Proxy objects, but OpenJPA uss a ChangeTracker on their Proxy objects to identify which objects in the SCO have been changed or modified.

How does DN identify which values in an SCO map need to updated if there is not equivalent ChangeTracker?  I cannot see/identify where/how within the DN Proxy objects DN is able to know which values need to be changed.

For instance, given a map of 1000 items, if I modify the value of one key, how does DN identify which key/value pair needs updating (as opposed to persisting the entire map again)?

Thanks,

Eric


Andy
 
Edited

It knows what to update based on what methods the user calls on the collection/map. Take the example of a field of type Collection, using
https://github.com/datanucleus/datanucleus-core/blob/master/src/main/java/org/datanucleus/store/types/wrappers/backed/Collection.java

The user does an "add" of an element, so calls https://github.com/datanucleus/datanucleus-core/blob/master/src/main/java/org/datanucleus/store/types/wrappers/backed/Collection.java#L645

If using optimistic txns (i.e delaying sending updates to the DB til required) it is a "queued update", so it calls
ownerSM.getExecutionContext().addOperationToQueue(new CollectionAddOperation(ownerSM, backingStore, element));
and otherwise (sending updates to the DB immediately) it calls backingStore.add(ownerSM, element, useCache ? delegate.size() : -1);

But then that is the whole point of using a proxy, it intercepts calls, and takes action as required. Current use-cases here are for persistence, and since it has all info that it needs for that no "original values" are explicitly stored there. Your use-case is different to what it is designed to cater for