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

Reply via email to