Re: Reconciling DataContexts

From: Kevin Menard (
Date: Tue Oct 16 2007 - 07:57:56 EDT

  • Next message: Andrus Adamchik: "Re: Reconciling DataContexts"

    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.


    On 10/16/07 9: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 >> >> >


    This archive was generated by hypermail 2.0.0 : Tue Oct 16 2007 - 07:58:35 EDT