persistence@glassfish.java.net

Re: Many-To-Many on JavaSE Collection returning null

From: Tom Ware <tom.ware_at_oracle.com>
Date: Wed, 04 Apr 2007 09:24:13 -0400

Hi David,

  In JPA, retreiving an object from the cache will retrieve the actual
instance that was added. Your call to children.clear() was clearing the
instance of the collection that was stored in your parent object. When
you callled find, your initial instance of parent was being retreived
(with the clear collection)

I'm glad you've got it working,
Tom

David Harrigan wrote:

>Hi,
>
>Bingo!
>
>children = new HashSet<Item>();
>
>and my test passes. So, what do you think is happening? That certainly
>is a gotcha for me! :-)
>
>Thanks.
>
>-=david=-
>
>On 4/4/07, Tom Ware <tom.ware_at_oracle.com> wrote:
>
>
>>I appologize if you've already tried this.
>>
>>What if you remove the call to children.clear() and instead create a new
>>instance of HashMap for your comparison.
>>
>>FYI: Yes, there is caching in TopLink essentials. Your call to find
>>will create a ReadObjectQuery that queries based on the primary key.
>>The first thing that query will do is check the cache for an item with
>>that primary key. If you want to force the query to go to the DB, try
>>doing an em.clear() after your initial commit().
>>
>>-Tom
>>
>>David Harrigan wrote:
>>
>>
>>
>>>Hi Tom, thanks for replying (you're probably v.busy :-) )
>>>
>>>Doesn't work. Here's the code:
>>>
>>> Item parent = itemFactory.getItem("Parent");
>>> Item child1 = itemFactory.getItem("Child 1");
>>> Item child2 = itemFactory.getItem("Child 2");
>>> Item child3 = itemFactory.getItem("Child 3");
>>> final Set<Item> children = new HashSet<Item>();
>>> final Set<Item> child1Items = new HashSet<Item>();
>>> final Set<Item> child2Items = new HashSet<Item>();
>>> final Set<Item> child3Items = new HashSet<Item>();
>>> children.add(child1);
>>> children.add(child2);
>>> children.add(child3);
>>> child1Items.add(parent);
>>> child2Items.add(parent);
>>> child3Items.add(parent);
>>> parent.setRelatedItems(children);
>>> child1.setItems(child1Items);
>>> child2.setItems(child2Items);
>>> child3.setItems(child3Items);
>>> em.getTransaction().begin();
>>> em.persist(parent);
>>> em.getTransaction().commit();
>>> assertEquals("Should not be '0'", true, parent.getItemId() != 0);
>>> assertEquals("Should not be '0'", true, child1.getItemId() != 0);
>>> assertEquals("Should not be '0'", true, child2.getItemId() != 0);
>>> assertEquals("Should not be '0'", true, child3.getItemId() != 0);
>>>
>>> // set all to null
>>>
>>> final int parentItemId = parent.getItemId();
>>>
>>> parent = null;
>>> child1 = null;
>>> child2 = null;
>>> child3 = null;
>>> children.clear();
>>>
>>> parent = em.find(Item.class, parentItemId)
>>>
>>> children.addAll(parent.getRelatedItems());
>>>
>>> assertEquals("Size should be 3", 3, children.size());
>>>
>>>It fails on the assert size == 3. Returns 0.
>>>
>>>I assume now that I'm wiring up both parts? The database looks the
>>>same as before. Interestingly enough (but maybe just a red-herring) is
>>>that I've set the logging to FINEST, yet I'm getting no SQL back, just
>>>these lines (note the last line, it's doing a ReadQueryObject - does
>>>this suggest some type of caching??? - it's not going off to the db??)
>>>
>>>[TopLink Fine]: 2007.04.04
>>>12:57:20.459--ClientSession(19167401)--Connection(31742556)--Thread(Thread[main,5,main])--INSERT
>>>INTO ITEM (ITEMID, MODIFIEDTIME, EXPIRYTIME, ITEMTYPE, RELEASETIME,
>>>DESCRIPTION, SOURCEFILENAME, COPYRIGHT, LOCATIONURI, AGECODE,
>>>SERVICEID, AUTHOR, CREATEDTIME, THIRDPARTYID, TITLE, TAGS_TAGSID,
>>>PROVIDER_PROVIDERID, RESTRICTION_RESTRICTIONID) VALUES (?, ?, ?, ?, ?,
>>>?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
>>> bind => [4, 2007-04-04 12:57:20.396, 2007-04-04 12:57:20.396, 0,
>>>2007-04-04 12:57:20.396, A descriptive entry, me.mpg, (C) Copyright
>>>2007, http://www.java.net, 18, 1, David Harrigan, 2007-04-04
>>>12:57:20.396, BBC01, Parent, 5, 7, 7]
>>>[TopLink Finest]: 2007.04.04
>>>12:57:20.460--UnitOfWork(7544464)--Thread(Thread[main,5,main])--Execute
>>>query DataModifyQuery()
>>>[TopLink Fine]: 2007.04.04
>>>12:57:20.460--ClientSession(19167401)--Connection(31742556)--Thread(Thread[main,5,main])--INSERT
>>>INTO ITEMRELATEDITEM (relatedItems_ITEMID, items_ITEMID) VALUES (?, ?)
>>> bind => [5, 4]
>>>[TopLink Finest]: 2007.04.04
>>>12:57:20.465--UnitOfWork(7544464)--Thread(Thread[main,5,main])--Execute
>>>query DataModifyQuery()
>>>[TopLink Fine]: 2007.04.04
>>>12:57:20.466--ClientSession(19167401)--Connection(31742556)--Thread(Thread[main,5,main])--INSERT
>>>INTO ITEMRELATEDITEM (relatedItems_ITEMID, items_ITEMID) VALUES (?, ?)
>>> bind => [6, 4]
>>>[TopLink Finest]: 2007.04.04
>>>12:57:20.466--UnitOfWork(7544464)--Thread(Thread[main,5,main])--Execute
>>>query DataModifyQuery()
>>>[TopLink Fine]: 2007.04.04
>>>12:57:20.467--ClientSession(19167401)--Connection(31742556)--Thread(Thread[main,5,main])--INSERT
>>>INTO ITEMRELATEDITEM (relatedItems_ITEMID, items_ITEMID) VALUES (?, ?)
>>> bind => [7, 4]
>>>[TopLink Finer]: 2007.04.04
>>>12:57:20.467--ClientSession(19167401)--Connection(31742556)--Thread(Thread[main,5,main])--commit
>>>transaction
>>>[TopLink Finer]: 2007.04.04
>>>12:57:20.469--UnitOfWork(7544464)--Thread(Thread[main,5,main])--end
>>>unit of work commit
>>>[TopLink Finer]: 2007.04.04
>>>12:57:20.470--UnitOfWork(7544464)--Thread(Thread[main,5,main])--resume
>>>unit of work
>>>[TopLink Finest]: 2007.04.04
>>>12:57:20.471--UnitOfWork(7544464)--Thread(Thread[main,5,main])--Execute
>>>query ReadObjectQuery(orange.media.service.model.Item
>>>
>>>-=david=-
>>>
>>>
>>>
>>>On 4/4/07, Tom Ware <tom.ware_at_oracle.com> wrote:
>>>
>>>
>>>
>>>
>>>>Hi David,
>>>>
>>>> What happens if you change your code to do the following:
>>>>
>>>>Item parent = new Item();
>>>>Item child = new Item();
>>>>Item child2 = new Item();
>>>>
>>>>children.add(child);
>>>>children.add(child2);
>>>>
>>>>child1Items.add(parent);
>>>>child2Items.add(parent);
>>>>
>>>>parent.setRelatedItems(children);
>>>>child1.setItems(child1Items);
>>>>child2.setItems(child2Items);
>>>>
>>>>em.getTransaction().begin();
>>>>em.persist(item);
>>>>em.getTransaction().commit();
>>>>
>>>> The JPA specification dictates that you have to maintain both sides of bidirectional relationahips yourself.
>>>>
>>>>-Tom
>>>>
>>>>
>>>>
>>>>David Harrigan wrote:
>>>>
>>>>
>>>>
>>>>
>>>>
>>>>>Hi,
>>>>>
>>>>>Java 6 (update 1).
>>>>>Glassfish v2 build = tracking cvs.
>>>>>
>>>>>Background:
>>>>>
>>>>>One class, doing a self-referential many-to-many (code has been cropped):
>>>>>
>>>>>@Entity
>>>>>public Class Item {
>>>>>
>>>>> @Id
>>>>> private int itemId;
>>>>>
>>>>> @ManyToMany(cascade = {PERSIST, MERGE, REFRESH})
>>>>> @JoinTable("ITEMRELATEDITEM")
>>>>> private Set<Item> relatedItems;
>>>>>
>>>>> @ManyToMany(mappedBy = "relatedItem")
>>>>> private Set<Item> items;
>>>>>
>>>>>}
>>>>>
>>>>>If I then do:
>>>>>
>>>>>Item parent = new Item();
>>>>>Item child = new Item();
>>>>>Item child2 = new Item();
>>>>>
>>>>>children.add(child);
>>>>>children.add(child2);
>>>>>
>>>>>parent.setRelatedItems(children);
>>>>>
>>>>>em.getTransaction().begin();
>>>>>em.persist(item);
>>>>>em.getTransaction().commit();
>>>>>
>>>>>All 3 entities get inserted into the database (assume ids, parent = 1,
>>>>>child = 2 and child = 3)
>>>>>
>>>>>In the ITEMRELATEDITEM table, these rows exist:
>>>>>
>>>>>RELATEDITEMS_ITEMID ITEMS_ITEMID
>>>>>2 1
>>>>>3 1
>>>>>
>>>>>Okay, subsequentially, if I now do the following:
>>>>>
>>>>>parent = null;
>>>>>child = null;
>>>>>child2 = null;
>>>>>
>>>>>parent = em.find(Item.class, 1);
>>>>>
>>>>>I get back the Parent Item object, but, if I then do:
>>>>>
>>>>>parent.getRelatedItems()
>>>>>
>>>>>this returns an empty set - always.
>>>>>
>>>>>Even if I put FetchType.EAGER on the two annotated fields.
>>>>>
>>>>>If I try this within a container (GF, current cvs), it works, in that
>>>>>getRelatedItems returns back a set with 2 items in it.
>>>>>
>>>>>Is there a problem with this in JavaSE?
>>>>>
>>>>>
>>>>>-=david=-
>>>>>
>>>>>
>>>>>
>>>>>
>>>>>
>>>>>
>>>>>
>>>>>
>>>
>>>
>>>
>>>
>
>
>
>