You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@openjpa.apache.org by David Ezzio <de...@ysoft.com> on 2007/09/24 16:30:24 UTC

Re: [Fwd: Re: Update & OneToMany]

Monday, September 24, 2007

Hi Esteve,

I've taken a look at the test case that you made available. I've 
reproduced the behavior that you are reporting. I can tell you where the 
problem lies, and I've attached code for you that does what your test 
case wants to do and works as expected.

So what's the problem in your test code?

In JPA, the basic life cycle of entities involves four states: new, 
managed, detached, or removed. Entities become detached under the 
following conditions (from the JPA spec):

"3.2.4 Detached Entities

"A detached entity may result from transaction commit if a 
transaction-scoped container-managed entity manager is used (see section 
3.3); from transaction rollback (see section 3.3.2); from clearing the 
persistence context; from closing an entity manager; and from 
serializing an entity or otherwise passing an entity by value—e.g., to a 
separate application tier, through a remote interface, etc."

Therefore, closing the entity manager, when the persistence context has 
extended scope (the default for J2SE), causes the managed entities to 
become detached.

In your example, you create a new Parent in the runTest1 method, make it 
persistent (managed), and put it in a map. You then close the entity 
manager. Viola! The parent stored in the map under "OBJ" tag has become 
detached.

In the next method, runTest2, you take this Parent and add two new 
children to it. The two children are new. They are not managed. You 
merge this object keeping the original detached object in the map. You 
close the entity manager. Result, 2 new children added to the database, 
in a one-to-many relationship to the Parent. BUT, the Parent in the map, 
although still detached, still has two new (not detached nor managed) 
children.

In the next method, runTest3, you take the original Parent, and add two 
more new children to it. You then merge the detached Parent again. 
Result, four more new children added to the database, which the Parent 
has a one-to-many relationship to, but the the Parent "forgets" about 
the relationship to the two children (still in the database) added in 
runTest2.

The problem is that when you merge, you get back from the merge method, 
a reference to the resulting managed object. If you take that reference 
and put it in the map, you'll have after commit and the closing of the 
entity manager, a detached reference to a Parent with two detached (NOT 
NEW) children. Then in step three, when you add two more children, you 
end up with just four children in the database, with no forgetten children.

If you had put a version column in the Parent, I believe it would have 
caught the duplicate use of the same detached version of the Parent and 
thrown an OptimisticLockException in runTest3 since that version had 
already been merged and committed with changes once before in runTest2.

Make sense?

Cheers,

David


Esteve Boix wrote:
> David,
> 
> I'm sorry to send a mail to your personal address... I've tried the
> mailing list, but no one replied:
> 
> --------------------------------
> 
> I've been experiencing a problem with OpenJPA.
> 
> Finally, I've been able to reproduce this outside my program, in an
> isolated test.
> You can download the test from:
> 
> http://www.esteveb.com/OpenJPATest.zip
> 
> (the mailing-list server keeps rejecting this mail as spam if I attach the file).
>  
> This is an small description of what is this doing:
> 
> Test1 method:
>     Create a new parent object
>     Store it in a map
>     Persist it
> 
> Test2 method:
>     Retrieve parent from map
>     Add 2 childs
>     Update it to database
> 
> Test3 method:
>     Retrieve parent from map
>     Add 2 childs more
>     Update it to database
> 
> After Test3, I end up with a child table with 6 childs. 2 from the first
> run (Test2), and 4 from the second run (Test3).
> 
> Debugging the program with Netbeans, I see that OpenJPA doesn't update
> the id of the childs when I persist de parent, thus in the next run,
> it thinks that the childs are new (that's my guess).
> 
> I'm using OpenJPA 1.0.0 on Java6, and Derby.
> 
> Regards,
> Esteve
> 
> 
> --------------------------------
> 
> Could you run the test and tell me if you're observing this behaviour or
> am I doing something terribly wrong ?
> 
> Best regards,
> Esteve
> 
> En/na David Ezzio (asmtp) ha escrit:
>> FYI
>>
>> -------- Original Message --------
>> Subject: [Fwd: Re: Update & OneToMany]
>> Date: Mon, 13 Aug 2007 12:54:29 -0400
>> From: David Ezzio (asmtp) <de...@bea.com>
>> To: users@openjpa.apache.org
>>
>> For some reason, the apache mail server mail server doesn't like this
>> message.  This is the 7th attempt.
>>
>> -------- Original Message --------
>> Subject: Re: Update & OneToMany
>> Date: Fri, 10 Aug 2007 18:24:20 -0400
>> From: David Ezzio (asmtp) <de...@bea.com>
>> To: users@openjpa.apache.org
>> References: <46...@esteveb.com>
>>
>> Hi Esteve,
>>
>> My test case does not reproduce the behavior that you are reporting. For
>> me, OpenJPA is working as expected, adding one new Address and one new
>> MESSAGE_ADDRESS entry. See abbreviated log below.
>>
>> I've attached the zip of the Eclipse project. I suggest that you try
>> reproducing what I'm reporting, then play with it to see whether it
>> starts to do what you are reporting. Let us know what you find out. I
>> did get one complaint on the @JoinColumn annotation which I removed. I
>> also simplified names and added the getters and setters, and created a
>> test case.
>>
>> Hope this helps,
>>
>> David
>>
>>
>> 1125  TRACE  [main] openjpa.jdbc.SQL - <t 27263487, conn 5294604>
>> executing prepstmnt 12985263
>>       INSERT INTO Address (id, address) VALUES (?, ?) [params=(long) 954,
>> (String) Route 66]
>>
>> 1140  TRACE  [main] openjpa.jdbc.SQL - <t 27263487, conn 5294604>
>> executing prepstmnt 59219
>>       INSERT INTO Address (id, address) VALUES (?, ?) [params=(long) 953,
>> (String) Autobahn]
>>
>> 1140  TRACE  [main] openjpa.jdbc.SQL - <t 27263487, conn 5294604>
>> executing prepstmnt 22221245
>>       INSERT INTO Address (id, address) VALUES (?, ?) [params=(long) 952,
>> (String) Route 1]
>>
>> 1140  TRACE  [main] openjpa.jdbc.SQL - <t 27263487, conn 5294604>
>> executing prepstmnt 11644607
>>       INSERT INTO Message (id, message, fromAddress_id) VALUES (?, ?, ?)
>> [params=(long) 951, (String) Hello, (long) 953]
>>
>> 1140  TRACE  [main] openjpa.jdbc.SQL - <t 27263487, conn 5294604>
>> executing prepstmnt 21465667
>>       INSERT INTO MESSAGE_ADDRESSES (MESSAGE_ID, ADDRESS_ID) VALUES (?,
>> ?) [params=(long) 951, (long) 952]
>>
>> 1140  TRACE  [main] openjpa.jdbc.SQL - <t 27263487, conn 5294604>
>> executing prepstmnt 29774358
>>       INSERT INTO MESSAGE_ADDRESSES (MESSAGE_ID, ADDRESS_ID) VALUES (?,
>> ?) [params=(long) 951, (long) 954]
>>
>> 1171  TRACE  [main] openjpa.jdbc.SQL - <t 27263487, conn 13121387>
>> executing prepstmnt 22878167
>>       SELECT t0.id, t1.id, t1.address, t0.message FROM Message t0 LEFT
>> OUTER JOIN Address t1 ON t0.fromAddress_id = t1.id
>>
>> 1171  TRACE  [main] openjpa.jdbc.SQL - <t 27263487, conn 13121387>
>> executing prepstmnt 9045316
>>       SELECT t1.id, t1.address FROM MESSAGE_ADDRESSES t0 INNER JOIN
>> Address t1 ON t0.ADDRESS_ID = t1.id
>>           WHERE t0.MESSAGE_ID = ? [params=(long) 951]
>>
>> 1187  TRACE  [main] openjpa.jdbc.SQL - <t 27263487, conn 17535609>
>> executing prepstmnt 20712275
>>       INSERT INTO Address (id, address) VALUES (?, ?) [params=(long) 955,
>> (String) Fleet Street]
>>
>> 1187  TRACE  [main] openjpa.jdbc.SQL - <t 27263487, conn 17535609>
>> executing prepstmnt 12413535
>>       INSERT INTO MESSAGE_ADDRESSES (MESSAGE_ID, ADDRESS_ID) VALUES (?,
>> ?) [params=(long) 951, (long) 955]
>>
>>
>> Esteve Boix wrote:
>>> David,
>>>
>>> Thanks for your fast response.
>>>
>>> I'm attaching the simplified relevant entites (no getters/setters and no
>>> irrelevant fields).
>>>
>>> The TemporalMessage have a OneToOne mapping to an address (guess it, the
>>> "Mail from:" :) ) and a OneToMany mapping to the same table (the "Rcpt
>>> to:").
>>>
>>> - I'm attaching the logs of what OpenJPA is doing.
>>> - I'm always accessing the collection (a List, actually) via the List
>>> interface, not using the setter to create a new List. I simply .add()
>>> new addresses and then I update the TemporalMessage.
>>> - The database is Derby in Client-Server mode.
>>>
>>> Best regards,
>>> Esteve
>>>
>>> PS: I'm attaching this as ZIP to try to circumvent the anti-ucm
>>> filter that
>>> keeps rejecting this mail...
>>>
>>> En/na David Ezzio ha escrit:
>>>> Hi Esteve,
>>>>
>>>> Doesn't sound normal.  Can you give us more info?  Some questions
>>>> that may help you understand what is going on.
>>>>
>>>> What are all the annotations for the OneToMany relationship?
>>>>
>>>> If Entity1 has a simple value attribute and you update that, does
>>>> the same behavior occur for the association?
>>>>
>>>> Is it possible that you have assigned a new collection to your
>>>> field, instead of modifying the existing collection?  If so, does
>>>> the behavior change if you change your application to modify the
>>>> existing collection?
>>>>
>>>> HTH,
>>>>
>>>> David Ezzio
>>>>
>>>> Esteve Boix wrote:
>>>>  
>>>>> Hi all,
>>>>>
>>>>> I'm observing the following behaviour, and I'd like to know if it's
>>>>> normal or if it's a config problem:
>>>>>
>>>>> I have an Entity1 with a @OneToMany relation to Entity2, using a table
>>>>> to link both entities. Everytime I update Entity1, OpenJPA deletes all
>>>>> the links from the link table and recreates all the entities Entity2.
>>>>> This is weird cause I end up with a lot of orphaned Entity2 objects
>>>>> (OpenJPA clears the links, but not the original entities).
>>>>>
>>>>> Regards,
>>>>> Esteve
> 
> 
> 

Re: [Fwd: Re: Update & OneToMany]

Posted by Esteve Boix <eb...@esteveb.com>.
Makes complete sense... I've tested your suggestion and OpenJPA works as
expected.

Anyway, it's weird that if you encapsulate the save/merge code in a DAO,
you have to return the new attached object.

It somehow invades the rest of the code, which is, well... ugly :), IMHO
of course.

Anyway, thanks a lot for all your time and help.

Regards,
Esteve

En/na David Ezzio ha escrit:
> Monday, September 24, 2007
>
> Hi Esteve,
>
> I've taken a look at the test case that you made available. I've
> reproduced the behavior that you are reporting. I can tell you where
> the problem lies, and I've attached code for you that does what your
> test case wants to do and works as expected.
>
> So what's the problem in your test code?
>
> In JPA, the basic life cycle of entities involves four states: new,
> managed, detached, or removed. Entities become detached under the
> following conditions (from the JPA spec):
>
> "3.2.4 Detached Entities
>
> "A detached entity may result from transaction commit if a
> transaction-scoped container-managed entity manager is used (see
> section 3.3); from transaction rollback (see section 3.3.2); from
> clearing the persistence context; from closing an entity manager; and
> from serializing an entity or otherwise passing an entity by
> value—e.g., to a separate application tier, through a remote
> interface, etc."
>
> Therefore, closing the entity manager, when the persistence context
> has extended scope (the default for J2SE), causes the managed entities
> to become detached.
>
> In your example, you create a new Parent in the runTest1 method, make
> it persistent (managed), and put it in a map. You then close the
> entity manager. Viola! The parent stored in the map under "OBJ" tag
> has become detached.
>
> In the next method, runTest2, you take this Parent and add two new
> children to it. The two children are new. They are not managed. You
> merge this object keeping the original detached object in the map. You
> close the entity manager. Result, 2 new children added to the
> database, in a one-to-many relationship to the Parent. BUT, the Parent
> in the map, although still detached, still has two new (not detached
> nor managed) children.
>
> In the next method, runTest3, you take the original Parent, and add
> two more new children to it. You then merge the detached Parent again.
> Result, four more new children added to the database, which the Parent
> has a one-to-many relationship to, but the the Parent "forgets" about
> the relationship to the two children (still in the database) added in
> runTest2.
>
> The problem is that when you merge, you get back from the merge
> method, a reference to the resulting managed object. If you take that
> reference and put it in the map, you'll have after commit and the
> closing of the entity manager, a detached reference to a Parent with
> two detached (NOT NEW) children. Then in step three, when you add two
> more children, you end up with just four children in the database,
> with no forgetten children.
>
> If you had put a version column in the Parent, I believe it would have
> caught the duplicate use of the same detached version of the Parent
> and thrown an OptimisticLockException in runTest3 since that version
> had already been merged and committed with changes once before in
> runTest2.
>
> Make sense?
>
> Cheers,
>
> David
>
>