JPA+hibernate merge vs find fetching lazy collection

K

kamiseq

hi,
I have very strange behaviour with my JPA entities.

I have to entities GenObject and GenObjectCnf that are linked with one-
to-many type, where in GenObject
@OrderBy
@OneToMany(cascade = CascadeType.ALL, mappedBy = "objects", fetch =
FetchType.LAZY)
private List<GenObjectCfg> objectsCfgSet = new ArrayList<GenObjectCfg>
();

now I create GenObject and persist it. then I instantiate new
GenObjectCnf object assign GenObject with it and persist. so in the db
everything is fine data is saved and I can move on. transaction is
over and my objects are detached from session.

the problem is now that my GenObject doesn't know about GenObjectCnf
as it was created before it and I didn't assigned GenObjectCnf back
after I created it. so I thought I can simply merge GenObject object
to update its state and data I need. so in other transaction Im
calling

this.em.merge(object);
GenObjectCfg parentCfg = object.getObjectsCfgSet().get(0);

as it should fetch all GenObjectCnf related to this GenObject object.
but is not and list remain empty.

BUT when I call
GenObject go = this.em.find(GenObject.class, object.getIdObject());
GenObjectCfg parentCfg = go.getObjectsCfgSet().get(0);

data is fetched and I get what I want, so essentially what am I
missing??
why is it so??

Im really out of ideas and it is something about how JPA works and I
didnt learn that yet, doesnt it?!
 
L

Lew

hi,
I have very strange behaviour with my JPA entities.

I have to entities GenObject and GenObjectCnf that are linked with one-
to-many type, where in GenObject
@OrderBy
@OneToMany(cascade = CascadeType.ALL, mappedBy = "objects", fetch =
FetchType.LAZY)
private List<GenObjectCfg> objectsCfgSet = new ArrayList<GenObjectCfg>
();

now I create GenObject and persist it. then I instantiate new
GenObjectCnf object assign GenObject with it and persist. so in the db
everything is fine data is saved and I can move on. transaction is
over and my objects are detached from session.

the problem is now that my GenObject doesn't know about GenObjectCnf
as it was created before it and I didn't assigned GenObjectCnf back
after I created it. so I thought I can simply merge GenObject object
to update its state and data I need. so in other transaction Im
calling

this.em.merge(object);
GenObjectCfg parentCfg = object.getObjectsCfgSet().get(0);

as it should fetch all GenObjectCnf related to this GenObject object.
but is not and list remain empty.

BUT when I call
GenObject go = this.em.find(GenObject.class, object.getIdObject());
GenObjectCfg parentCfg = go.getObjectsCfgSet().get(0);

data is fetched and I get what I want, so essentially what am I
missing??
why is it so??

Im really out of ideas and it is something about how JPA works and I
didnt learn that yet, doesnt it?!

I don't know your answer yet, but two things puzzle me so far, plus
can you provide an SSCCE <http://sscce.org/>?

Puzzle 1: Why are you explicitly allocating an ArrayList to
'objectsCfgSet'?
Puzzle 2: What is 'objects' in the 'GenObjectCfg' type? (This is one
place where an SSCCE would help.) That the name is plural but it's
mapping what presumably is a many-to-one from 'GenObjectCfg' is
suspicious.

An SSCCE might be a little tricky here; if so I understand. But at
the least give us complete (enough) source for 'GenObject' and
'GenObjectCfg', and the DDL for the associated tables.

Also, you didn't need to post your question twice. Someone eventually
will get around to answering, or they won't.
 
T

Tom Anderson

Puzzle 1: Why are you explicitly allocating an ArrayList to
'objectsCfgSet'?

Because otherwise, it would be null, and so any attempt to use the object
before it was persisted would fail. No?

tom
 
A

Arved Sandstrom

kamiseq said:
hi,
I have very strange behaviour with my JPA entities.

I have to entities GenObject and GenObjectCnf that are linked with one-
to-many type, where in GenObject
@OrderBy
@OneToMany(cascade = CascadeType.ALL, mappedBy = "objects", fetch =
FetchType.LAZY)
private List<GenObjectCfg> objectsCfgSet = new ArrayList<GenObjectCfg>
();

now I create GenObject and persist it. then I instantiate new
GenObjectCnf object assign GenObject with it and persist. so in the db
everything is fine data is saved and I can move on. transaction is
over and my objects are detached from session.

the problem is now that my GenObject doesn't know about GenObjectCnf
as it was created before it and I didn't assigned GenObjectCnf back
after I created it. so I thought I can simply merge GenObject object
to update its state and data I need. so in other transaction Im
calling

this.em.merge(object);
GenObjectCfg parentCfg = object.getObjectsCfgSet().get(0);

as it should fetch all GenObjectCnf related to this GenObject object.
but is not and list remain empty.

BUT when I call
GenObject go = this.em.find(GenObject.class, object.getIdObject());
GenObjectCfg parentCfg = go.getObjectsCfgSet().get(0);

data is fetched and I get what I want, so essentially what am I
missing??
why is it so??

Im really out of ideas and it is something about how JPA works and I
didnt learn that yet, doesnt it?!

A couple of notes:

1) The mappedBy name "objects" is jarring. If I understand you
correctly, you're saying that for one GenObject we have many GenObjCnf
objects. In the database the GenObjCnf table will have a foreign key for
GenObject, and GenObjCnf is considered to be the owning side; GenObject
is the inverse side, and has the mappedBy attribute, which you have
correctly. "objects" is the field in each GenObjCnf that refers to the
single GenObject, so would be something like

GenObject genObject;

and you'd more likely declare it as

mappedBy="genObject"

2) the declaration of the list List<GenObjCnf> objectsCfgSet is sort of
OK (better not to call it a list or a set, just call the variable
"cfgObjects" or suchlike).

3) EntityManager.merge() doesn't update state - calling refresh() or
explicitly changing state on the POJO instance does.

4) do you know for a fact that after persisting that the DB is updated?
Are you looking at console SQL or keeping an eye o the tables? What are
your transactions like? Are you using application-managed or
container-managed entity managers?

5) with CascadeType.ALL you could have just created the GenObject,
created a GenObjCnf, added the GenObjCnf to the list, then persisted
only the GenObject. That's what cascades are for.

6) Lazy initialization is not pinned down by the JPA spec, leastways not
JPA 1.0. Use at your own risk; persistence implementations have a lot of
leeway. If you are newish to JPA, consider setting the OneToMany to
EAGER (note that the default _is_ LAZY for that type of relationship, so
you didn't need to put that in) and get stuff working.

7) As pointed out by others, it's your responsibility to a great extent
to maintain relationships in code; JPA implementations take care of the
database. For example, if you were to call em.remove on one of the
GenObjCnf objects, would you expect Hibernate to take it out of the
collection?

8) As others have asked for, provide a short SSCCE. I believe we can
help you figure this out.

AHS
 
K

kamiseq

1) The mappedBy name "objects" is jarring. If I understand you
correctly, you're saying that for one GenObject we have many GenObjCnf
objects. In the database the GenObjCnf table will have a foreign key for
GenObject, and GenObjCnf is considered to be the owning side; GenObject
is the inverse side, and has the mappedBy attribute, which you have
correctly. "objects" is the field in each GenObjCnf that refers to the
single GenObject, so would be something like

GenObject genObject;

and you'd more likely declare it as

mappedBy="genObject"
sure but it was all auto generated by netbeans.
4) do you know for a fact that after persisting that the DB is updated?
Are you looking at console SQL or keeping an eye o the tables? What are
your transactions like? Are you using application-managed or
container-managed entity managers?
this all fine
3) EntityManager.merge() doesn't update state - calling refresh() or
explicitly changing state on the POJO instance does.

6) Lazy initialization is not pinned down by the JPA spec, leastways not
JPA 1.0. Use at your own risk; persistence implementations have a lot of
leeway. If you are newish to JPA, consider setting the OneToMany to
EAGER (note that the default _is_ LAZY for that type of relationship, so
you didn't need to put that in) and get stuff working.
(ok but then it is a problem again with List collection - so I prefer
to stay with Lazy fetch)

my question was more about why

GenObject go = this.em.find(GenObject.class, object.getIdObject());
GenObjectCfg parentCfg = go.getObjectsCfgSet().get(0);

works fine (so all mappings between entities are correct and data in
DB is correct) where

GenObject object = this.em.merge(object);
GenObjectCfg parentCfg = object.getObjectsCfgSet().get(0);

is not working at all. (ok refreshing object this.em.refresh(object);
solves the problem and data is fetched).

all I dont understand is - if my entity(GenObject) has lazily fetched
collection and it is detached from session then to start working with
that entity I need to first attach it to the session (em.merge()) then
I can normally fetch the collection from the db, cannot I?for me it
doesn't matter how I obtain/create the entity(GenObject). I can
em.find that entity and return it to the client (entity's collection
is empty), the client wants to fetch more information about related
objects(GenObjectCfg) so the GenObject entity is sent back, merged and
I should be able to retrieve GenObjectCfg objects list without
refreshing GenObject.
 
A

Arved Sandstrom

kamiseq said:
sure but it was all auto generated by netbeans.

NetBeans is pretty good at this, but it never hurts to review the
generated code. Provided that what NB has generated fits in with my
explanation above, you're cool.
this all fine

>
(ok but then it is a problem again with List collection - so I prefer
to stay with Lazy fetch)

my question was more about why

GenObject go = this.em.find(GenObject.class, object.getIdObject());
GenObjectCfg parentCfg = go.getObjectsCfgSet().get(0);

works fine (so all mappings between entities are correct and data in
DB is correct) where

GenObject object = this.em.merge(object);
GenObjectCfg parentCfg = object.getObjectsCfgSet().get(0);

is not working at all. (ok refreshing object this.em.refresh(object);
solves the problem and data is fetched).

all I dont understand is - if my entity(GenObject) has lazily fetched
collection and it is detached from session then to start working with
that entity I need to first attach it to the session (em.merge())

This is correct. merge() is for bringing a detached entity back under
management in a persistence context. As an aside, em.contains(entity)
will tell you whether the context already has it managed...depending on
whether you are using container-managed or application-managed EMs (and
in the former case, whether you have transaction-scope or extended
scope) it may well be that sometimes an entity is still managed when you
may think that it's detached. Just something to keep in mind.
then
I can normally fetch the collection from the db, cannot I?for me it
doesn't matter how I obtain/create the entity(GenObject). I can
em.find that entity and return it to the client (entity's collection
is empty), the client wants to fetch more information about related
objects(GenObjectCfg) so the GenObject entity is sent back, merged and
I should be able to retrieve GenObjectCfg objects list without
refreshing GenObject.

As an experiment (this is a fairly common trick) try calling size() on
that lazy collection, see what it returns.

AHS
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,766
Messages
2,569,569
Members
45,045
Latest member
DRCM

Latest Threads

Top