Nice to see that my (too?) elaborate story has helped you. Indeed a UUID should solve this problem. (I considered this myself but did not use one because it shifts the responsibility of assigning the id to the client and I wasn't really sure if this is a good idea.)
Normally I would simply merge/persist C and let OpenJPA handle merging A by cascading but I guess you are still left with the problem that B references C as well and depending on the model B may not be cascaded. -----Oorspronkelijk bericht----- Van: Jim Talbut [mailto:jtal...@spudsoft.co.uk] Verzonden: donderdag 27 september 2012 22:12 Aan: users@openjpa.apache.org Onderwerp: Re: How to add children to disconnected entity? Thanks for that. What I was doing was to persist C and then merge A, which resulted in C becoming C', when I then persisted B OpenJPA could find no connection between C and C'. The C entities only get an ID when they are persisted. I was hoping that C' would retain knowledge of C, which would let my scenario work, but it doesn't seem to. My fix was to add a UUID to C and store that in B instead of C itself. If I was starting again I would have made the UUID the ID, but the database already exists and changing the PK is too much at this stage. Jim On 27/09/2012 09:27, Henno Vermeulen wrote: > Hi, > > Not sure I exactly understand the situation. Perhaps you are using cascade > merge on both of the objects that refer to it? > Normally you shouldn't use cascade merge when it is only an association and > use cascade merge when the entity really "owns" the other entity. > > Suppose A and B both refer to C (A -> C, B -> C) and C is owned by A. > Then the relation A -> C should use cascade merge but B -> C should not. > If C is an existing entity and you merge A, C will be merged as well. If you > then merge B which refers to the same C, this should succeed even if the > version of C contained in B is an old one. The relation B -> C does not use > cascade merge so when merging B only the identity of C matters. (If it would > cascade merge you would get an optimistic lock exception if you use that or > otherwise you could overwrite C with old data). > > Perhaps you are in the situation where you are using id generated by OpenJPA > and C is not yet persisted. I always regard two objects that are not > persistent but have exactly the same data to be different from each other. > The reason is that the act of merging does not change the parameter to merge. > Instead it returns an attached object which represents the same logical > entity in the database but does not refer to the same object in memory. I > always regard merge as a save operation that does not change the parameter so > I throw away the entity passed-in and work with the retuned one. I always > take care to first merge an unpersisted entity before I use it in an > associaton with another entity. > > Detailed explanation of what happens: when B refers to a "clone" of the > unpersisted C there is absolutely no way for OpenJPA to recognize that B has > a reference to the same C as A has. Suppose you start out with an A and B > referring to the same unpersisted C in memory. You then merge A and this > cascade merges C. The result of the merge operation will contain a now > persistent copy of C but the original unpersisted C object in memory is not > changed, i.e. the C that results from the merge refers to another object in > memory. If you then merge B you should get an exception that the relation > does not allow cascade because B still refers to the unpersisted C. However > if the relation B -> C was set to cascade merge, then you will end up with B > referring to another new instance that has the same data as C but does not > have the same database identity. > > Hope this explanation helps, even though it may not be your exact situation. > The moral of the story that I learned over time is to realize that merge does > not alter its parameter so that you should really regard two unpersisted > clones as different. And it's usually better to use cascade merge only on one > relation, i.e. from it's owner to the entity. This will prevent the bugs that > you can silently get a duplicate or merge an older version. > > -----Oorspronkelijk bericht----- > Van: Jim Talbut [mailto:jtal...@spudsoft.co.uk] > Verzonden: woensdag 26 september 2012 23:05 > Aan: users@openjpa.apache.org > Onderwerp: Re: How to add children to disconnected entity? > > On 26/09/2012 10:57, Jim Talbut wrote: >> On 26/09/2012 10:19, Henno Vermeulen wrote: >>> Hi, >>> >>> I verified this situation by making an extra unit test in our system. >>> We always work with detached entities as well. The test works fine >>> for me. >>> >>> One explanation for this behavior is that the @OneToMany field does >>> not use "fetch = FetchType.EAGER" because relations are lazily >>> fetched by default. (Or alternatively you have not included the field >>> in OpenJPA's FetchPlan before calling entityManager.merge which has >>> the same effect as FetchType.EAGER even if the @OneToMany field is >>> not eager). >>> >>> When I adjust my test to lazily fetch the field, then saving a new >>> Assessment somehow DOES cascade merge new AssessmentResults. However >>> when I merge an existing Assessment that has one existing >>> AssessmentResult and one new AssessmentResult, OpenJPA will not >>> cascade merge it. >>> >> Thank you. >> Yes, that is precisely what I'm going to be doing. >> The results are large, so they are Lazy loaded, and the assessments >> are created in a different transaction - so the list is always empty >> when first loaded. >> >> What I'm doing at the moment is, in the function that calls merge, >> iterating through the results and persisting any that aren't contained >> or detached. >> I have to do that first (before merging the assessment) or some >> results get persisted twice. >> This seems to be working (well, one of my unit tests passed, I've got >> other unit tests failing and that may not be related to this). >> >> Jim > I've got that nearly all working now, but I've still got one big problem. > > The detached entity that I create has two objects referring to it: one > of which gets merged and the other gets persisted. > The act of merging attaches the new entity, but replaces the variable > the refers to it. > So when the other entity gets persisted I end up with a duplicate entity. > > This is all caused by trying to break down big transactions into much > smaller ones, which is why it's not the best structure. > > Jim