Re: Reconciling DataContexts

From: Michael Gentry (blacknex..mail.com)
Date: Tue Oct 16 2007 - 09:18:52 EDT

  • Next message: Tore Halset: "db migrator"

    Kevin,

    Could a nested context help with your situation?

    /dev/mrg

    On 10/16/07, Kevin Menard <kmenar..ervprise.com> wrote:
    > The problem with localObject is that it does not traverse the whole graph,
    > making it practically useless in all but the most trivial situations.
    > Manually copying over the graph is doable, but a lot of work for something
    > that really should be far easier.
    >
    > For example, we have customers and orders. All orders are associated with
    > customers. We keep the customer info and order info in two different
    > contexts for the most part, because we don't want to prematurely commit an
    > order while a customer may feel it necessary to change his shipping address,
    > for example.
    >
    > What we've resorted to doing is committing the customer data, then doing a
    > requery based on the ObjectID using the order context. This allows us to
    > get a fully populated object. Of course, this means more trips to the DB
    > and a lot of committing/refetching code. It works, but it hardly makes
    > Cayenne transparent.
    >
    > The whole thing becomes fragile, and there's a lot that Cayenne could do to
    > help out. I suppose the problem is exacerbated by the null context problem.
    > If we fix that, we'd likely be in much better shape. As it stands, with our
    > Tapestry app, we have to stage the objects at page render time to allow the
    > association of different objects. That creates for a lot of headaches as
    > well, and a lot of context management that really isn't necessary.
    >
    > --
    > Kevin
    >
    >
    > On 10/16/07 9:02 AM, "Andrus Adamchik" <andru..bjectstyle.org> 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
    > >>
    > >>
    > >
    >
    > --
    >
    >
    >



    This archive was generated by hypermail 2.0.0 : Tue Oct 16 2007 - 09:19:27 EDT