From: Arved Sandstrom on
Tom Anderson wrote:
> On Tue, 29 Dec 2009, Lew wrote:
>
>> Lew wrote:
>>>> Puzzle 1: Why are you explicitly allocating an ArrayList to
>>>> 'objectsCfgSet'?
>>
>> kamiseq wrote:
>>> why not? and plus you dont need to check every time if collection is
>>> null or not. this is not a problem here
>>
>> Why not? Because it's atypical of JPA code and I haven't seen that
>> done. Usually you either let the JPA engine fill it
>
> Which won't happen before the JPA provider has got its hands on it, so:
>
> new GenObject().objectsCfgSet.add(something);
>
> Will fail, right?
>
> There's a general principle (more honoured in the breach than in the
> observance) that JPA entities should be fully functional POJOs, ie
> should work correctly even without JPA. Or, perhaps, that they *can* be,
> and since this simplifies unit testing, some pundits elevate that to
> *should*.
[ SNIP ]

One area where you can have non-POJO behaviour is with the area I called
out: lazy initialization. I've seen weird behaviour in some JPA
implementations because of the various backing collections used in 1:n
relationships, for example. Even if you call this misbehaviour defects -
I would - it's still something to look out for with implementation of
lazy initialization.

Defects also appear in at least one implementation of cascades. You can
always do without cascades so this is not a showstopper.

And finally, with caching all bets are off. Your code will almost
certainly behave differently in this respect for each JPA provider you try.

AHS
From: Lew on
Tom Anderson wrote:
Lew wrote:
>>>> Puzzle 1: Why are you explicitly allocating an ArrayList to
>>>> 'objectsCfgSet'?

kamiseq wrote:
>>> why not? and plus you dont need to check every time if collection is
>>> null or not. this is not a problem here

Lew wrote:
>> Why not? Because it's atypical of JPA code and I haven't seen that
>> done. Usually you either let the JPA engine fill it

Tom Anderson wrote:
> Which won't happen before the JPA provider has got its hands on it, so:
>
> new GenObject().objectsCfgSet.add(something);
>
> Will fail, right?

Well, duhh. You're supposed to pass in the List, not depend on it being
there. That's why there's a setter for it.

And it will certainly fail if 'objectsCfgSet' were properly declared as 'private'.

> They're using setters because they're using property access - note the
> @Id annotation on the getCustomerID() method rather than the id field.
> That means they have to use setters to set fields. Kamiseq is using
> field access, which means he can access the fields directly. There's no
> JPA-specific reason for him to use a setter rather than direct access.

There is a common idiom, universal to all the training literature on JPA, to
have setters for even the collections. Furthermore, when you set up
field-level annotations you still have bean patterns for the properties, and
the variables remain 'private'. Your point does not apply.

It's not that I think that initialization was wrong, necessarily. As I
stated, it's atypical. Not one bit of training literature that I've read has
done that, and not one bit of the JPA code I've worked with has, either. So
it looked odd, and I wondered why he did it. A perfectly reasonable question,
when you encounter an idiom that none of the experts use.

Now I'm wondering why the OP took the question as an attack instead of a
request for information.

Anyway, once you persist the object, the initially-allocated List goes away
and you lose control of whether it's null. So the point of not having to
check if the collection is null doesn't apply, either.

I think it's best to stick with the way JPA was designed, and I infer from the
universal absence of collection initialization in the training literature that
it was designed not to need it. I've seen what can happen to ORM code when it
deviates from the intended idioms. You introduce epicycles that get ever more
rococo. Entity objects are not really meant for a lot of manipulation outside
the JPA context or by application logic. The more complicated you get with
them, the weirder the behaviors you start to observe.

I recommend not initializing the @OneToMany and @ManyToMany collections, but
simply letting JPA do its job.

As a rule.

--
Lew
From: Arved Sandstrom on
Lew wrote:
[ SNIP ]

> Anyway, once you persist the object, the initially-allocated List goes
> away and you lose control of whether it's null. So the point of not
> having to check if the collection is null doesn't apply, either.

That's the main thing. Why bother assigning your own collection to a
variable when JPA is going to completely ignore it.

In a typical situation where you'd be creating a collection from scratch
- namely, you've just created the parent so you know that there can't be
any children, and you do have some children to persist at the same time,
you'd typically set the new collection through the setter anyhow.

> I think it's best to stick with the way JPA was designed, and I infer
> from the universal absence of collection initialization in the training
> literature that it was designed not to need it. I've seen what can
> happen to ORM code when it deviates from the intended idioms. You
> introduce epicycles that get ever more rococo. Entity objects are not
> really meant for a lot of manipulation outside the JPA context or by
> application logic. The more complicated you get with them, the weirder
> the behaviors you start to observe.

At the risk of beating a dead horse, I have observed that lazy
initialization causes the most complications. I've learned to analyze
usage and if it's clear that LAZY is situationally unnecessary, to not
use it.

Along the same lines I tend to to use cascades a lot less than I used to
- they are just a coding convenience, and in reality come in useful a
lot less than a person might think...but if haphazardly imposed on
relationships "just in case", can cause problems.

> I recommend not initializing the @OneToMany and @ManyToMany collections,
> but simply letting JPA do its job.
>
> As a rule.

Agreed.

AHS

P.S. I expect our "pure OOP, OODBMS roolz" friend to be frothing at the
mouth just about now. :-)
From: Arved Sandstrom on
kamiseq wrote:
>> 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.

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.

>> 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())

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
From: kamiseq on
> >>>> Puzzle 1: Why are you explicitly allocating an ArrayList to
> >>>> 'objectsCfgSet'?
> kamiseq wrote:
> >>> why not? and plus you dont need to check every time if collection is
> >>> null or not. this is not a problem here
> It's not that I think that initialization was wrong, necessarily.  As I
> stated, it's atypical.  Not one bit of training literature that I've read has
> done that, and not one bit of the JPA code I've worked with has, either.

cos it doesnt hurt :) in my application I am focused on collection
itself. All I care about id if collection has 0 or more items. so if
didn't initialize with ArrayList then there would be a situation when
collection is null as JPA will not insert empty collection. from JPA
and application point of view null reference is exactly the same as
empty collection. but if collection is already there I don't need to
check in my code for nulls. if you want to select, add or remove item
or list items you need to be sure that collection is initialized. I
still doesnt understand where this can be a problem and this is common
pattern I and my friends apply