BTW, note that JPA "merge" returns a merged object, so it is exactly
like "localObject" in that it locates a copy of an object local to
the context.
Andrus
On Oct 16, 2007, at 10:02 AM, Andrus Adamchik wrote:
Hi Kevin,
Relating (as in "creating a direct reference in a Java sense") two
objects belonging to two distinct contexts breaks fundamental
Cayenne design, most notably assumptions about uniquing and
transaction boundaries. I am -1 on that until somebody persuades me
that this is a good idea and explains how to avoid messing up
existing assumptions. (FWIW there is a workaround - referencing a
peer of a given object in another context via 'localObject').
On the other hand the inability to relate two TRANSIENT objects
(i.e. objects without a context) is indeed a shortcuming. Here is
one way how it might work (following the JPA patterns) :
When such relationship is established, we would not attempt to
create a reverse relationship (something that would require an
EntityResolver to be present). We just create it one-way. So a user
can build an object graph of an arbitrary size without a context
and then at some point do one of the following with it:
* "ObjectContext.registerNewObject(..)" (existing; equivalent to
JPA "persist" method)
* "ObjectContext.aNewMethod(..)" (does not exist yet; equivalent to
JPA "merge" method and somewhat of an equivalent of a "localObject"
method).
Both would traverse a graph of transient objects (since they are
not persistent yet, the graph is assumed to be finite and will not
trigger sucking the entire DB in memory), attach them to the
context and connect missing reverse relationships. The difference
is that in the first case we'd assume that the objects are not yet
persistent, while in a second case we'd attempt to match them
against existing DB rows. (a typical use of a second case would be
receiving XML-serialized stream of objects corresponding to the
existing data).
But you have something different in mind? Could you elaborate on
the use cases?
Thanks
Andrus
On Oct 16, 2007, at 1:25 AM, Kevin Menard wrote:
So, if there is one thing that drives me nuts about Cayenne, it's
managing
ObjectContexts. In particular, you cannot relate two Persistent
objects
without first putting them into an ObjectContext. If one is
committed and
the other is not, you can have them in different contexts, but for
newly
created objects, this is a major pain in the neck.
Since I've been complaining about it for probably close to three
years now,
I'd like to finally do something about it.
Here are my rough notes from the airport:
OK Cases:
- Objects in same context
- Objects in different contexts, but objects are committed already
Don't work, but should:
- Objects in null contexts
- Objects in different contexts, but same data maps and domains
Very hard to say, probably okay if don't work:
- Objects in different contexts, contexts have different data maps
- Objects in different contexts, contexts have different data domains
As I started actually digging into the code, I ran into a lot of
NPE issues
trying to associate two Persistent objects with no context with
one another.
In an attempt to prevent adding special null-logic handling, I
thought about
applying the Null Object pattern to the problem. The basic idea
is rather
than use null as the default objectContext for CDO, use an
instance of
DelayedContext. The problem here is having objects in different
contexts.
So, it appears by fixing one, you can essentially fix the other.
To address the latter, I was looking to have a Set of
ObjectContexts stored
in either BaseContext or DataContext. When willConnect() is
called, you'd
have something like the following:
else if (this.getObjectContext().getEntityResolver() ==
object.getObjectContext().getEntityResolver()) {
((DataContext)
this.getObjectContext()).addContextToMergeWith
(object.getObjectContext());
((DataContext)
object.getObjectContext()).addContextToMergeWith
(this.getObjectContext());
}
else {
throw new CayenneRuntimeException(
"Cannot set object as destination of
relationship "
+ relationshipName
+ " because it is in a different
DataMap or
DataDomain.");
}
(Casts are just an artifact of me screwing around).
What I'm thinking would happen is that when commitChanges() is
called, the
set of contexts to be merged with will be iterated and any changes
applied
to the current object store / object store graph diff. The
relationship is
bidirectional so that the user can initiate the commit from any
object
registered with any context.
Here is about where I lose it. I'm not as well-versed in the
internal
going-ons of Cayenne as I would like to be. It appears Cayenne
goes to
great efforts to essentially cache the graph manipulations so as
to avoid a
full traversal. I really don't know, though.
Caching ordering of operations could make this tricky, but in
principal
should be wholly doable.
If anyone has any thoughts on this or can fill in any missing
pieces, I'd
appreciate it. This is really something I'd like to see fixed
sooner rather
than later. I think it may be a requisite for JPA compliance as
well.
--
Kevin