You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@openjpa.apache.org by Jim Talbut <jt...@spudsoft.co.uk> on 2012/09/26 00:02:23 UTC

How to add children to disconnected entity?

Hi,

I have a pair of entities: Assessment & AssessmentResults, with a 
One-to-Many relationship (an assessment can have many results).
The Assessment is modified whilst disconnected (outside of a transaction).

Is there any way to have AssessmentResults added whilst the Assessment 
is disconnected be persisted automatically when the Assessment gets merged?
Setting CascadeType.Merge doesn't seem to do it (I think that merges 
existing children, not adds new children).

Thanks

Jim


RE: How to add children to disconnected entity?

Posted by Henno Vermeulen <he...@huizemolenaar.nl>.
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:jtalbut@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:jtalbut@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


Re: How to add children to disconnected entity?

Posted by Jim Talbut <jt...@spudsoft.co.uk>.
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:jtalbut@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


RE: How to add children to disconnected entity?

Posted by Henno Vermeulen <he...@huizemolenaar.nl>.
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:jtalbut@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

Re: How to add children to disconnected entity?

Posted by Jim Talbut <jt...@spudsoft.co.uk>.
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

Re: How to add children to disconnected entity?

Posted by Jim Talbut <jt...@spudsoft.co.uk>.
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

RE: How to add children to disconnected entity?

Posted by Henno Vermeulen <he...@huizemolenaar.nl>.
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.

-----Oorspronkelijk bericht-----
Van: Jim Talbut [mailto:jtalbut@spudsoft.co.uk] 
Verzonden: woensdag 26 september 2012 0:02
Aan: 'users@openjpa.apache.org'
Onderwerp: How to add children to disconnected entity?

Hi,

I have a pair of entities: Assessment & AssessmentResults, with a 
One-to-Many relationship (an assessment can have many results).
The Assessment is modified whilst disconnected (outside of a transaction).

Is there any way to have AssessmentResults added whilst the Assessment 
is disconnected be persisted automatically when the Assessment gets merged?
Setting CascadeType.Merge doesn't seem to do it (I think that merges 
existing children, not adds new children).

Thanks

Jim