Hi Andrus,
I'd go with the quick-fix for now (modeler) and maybe explore a better
solution in the future. It is better to have them deal with a
safe/working restriction and not have potential errors. Worst case,
if they want the artistId, they can follow the Artist relationship and
get the ID from there. They can even add a cover method in
Painting.java:
public int getArtistId()
{
return getArtist().getId();
}
mrg
On Wed, Oct 19, 2011 at 3:02 AM, Andrus Adamchik <[email protected]> wrote:
> Just coming of a few months (!) of low intensity debugging of a single
> problem that looked as if Cayenne resets a non-null to-one relationship to
> null. The FK would be not null in the DB, but all instances of a given object
> across all DataContexts in a given app would return null for that one
> relationship. Turned out this wasn't an obscure race condition, but rather
> the following scenario:
>
> 1. Assume Artist and Painting with painting having to one relationship called
> "artist", but also an ObjAttribute called "artistId"
> 2. For an existing Artist you may create a new painting, call "setArtist",
> but don't set the "artistId" attribute explicitly.
> 3. Commit - that creates a valid record with non-null PAINTING.ARTIST_ID in
> the db, but the object snapshot stored in DataRowStore suddenly has NULL for
> "ARTIST_ID" key.
> 4. From here all DataContexts that fault this painting from the shared cache
> will have NULL "artist" relationship, even though it is not null in the DB.
>
> The actual behavior during (3 - commit) I think is less deterministic and
> depends on the relative order of traversal of entity properties in the
> ClassDescriptor. So theoretically there may have been a reverse situation
> when NULL was saved to the DB, but not-null value remained in the snapshot.
> In any event it is extremely confusing.
>
> If we take a broader look at this problem, it is a case of redundant mapping
> (something in the DB layer is mapped multiple times in the object layer).
> There can be other cases with yet unknown sets of problems (e.g. multiple
> ObjRelationships over the same DbRelationship; flattened relationships, with
> a matching set of 1-step relationships; etc).
>
> We only have a single case where redundant mapping is handled correctly and
> consistently - exposed PK (meaningful or not) . Even if the exposed PK is
> generated by Cayenne, we have a mechanism to update object property. So this
> is a case when we do it right, and don't compromise on user choices of
> mapping scenarios.
>
> In case of exposed FK we have 2 options - (1) just add a Modeler validation
> to discourage this type of mapping (quick and easy) and (2) actually analyze
> the above and other possible scenarios when we need to synchronize
> relationship and attribute and write code to do that. #2 can be done as a
> post-commit pass over the objects to sync redundant mappings... Just not
> clear which one of the redundant mappings should be used to sync the others...
>
> Thoughts?