You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@openjpa.apache.org by Laird Nelson <lj...@gmail.com> on 2009/08/13 18:33:00 UTC

insertable/updatable = false question

I have an entity, E2, that contains a many-to-one relationship with E1 like
this:

// assume E1's primary key column is x
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "x", referencedColumnName = "x", insertable = false,
updatable = false)
private E1 e1;

Then in E2 I also have a field like this:

@Column(name = "x")
private int x;

When I first create an instance of E2, I set x to, say, 5.  Let's assume we
know that E1 exists in the database under a primary key of x = 5 as well.

If I examine the unpersisted/transient E2 instance, I can see that x indeed
is 5, and e1 is null, as I'd expect.

Now--we're on the server side for all this--I do this:

e2 = this.em.merge(e2);
this.em.flush();

After the flush, I would expect that:
e2 would be persisted
e2 would have its x = 5 (unchanged)
e2.e1 would be NON NULL.  That is if I did: e2.getE1(); I'd get back a
non-null return value.

But instead I am seeing that e2.e1 is null.

Is this expected behavior?

What I'm looking for is the ability for someone to create an E2 without an
E1 in hand, pass it in to my EJB method, where I will--using lazy
loading--"inflate" the E1 relationship.  I can't figure out how to make this
happen.

Thanks,
Laird

Re: insertable/updatable = false question

Posted by ljnelson <lj...@gmail.com>.
On Fri, Aug 14, 2009 at 12:56 PM, Michael Dick (via Nabble) <
ml-user+17961-1519918167@n2.nabble.com<ml...@n2.nabble.com>
> wrote:

> I haven't tested any of this out, but I'd guess that OpenJPA (and
> apparently
> other providers) have loaded your @ManyToOne relationship before you call
> the accessor method.


You know, that's an excellent point.  I wonder.

If the field was loaded by the provider there may be no reason to think that
>
> it's dirty and we need to fetch it from the database (em.refresh will force
>
> a reload).


Here's the thing, though (continuing my earlier example running through this
thread; see earlier messages for details).  If I merge e2, with its "x"
field set, but not its e1 field, which, yes, is lazy, but is also insertable
= false and updatable = false, then there is no circumstance that I can
think of where the e1 state SHOULDN'T be reloaded.

Do you see what I mean?  If I can't insert or update my e1 field, then why
wouldn't the provider be required, upon a merge, to load the e1 instance
from disk at merge time?


> It's possible that we could detect this case. Basically you have two fields
>
>  (read-only and read-write) in an entity mapped to the same column. Once
> the
> RW field has been updated we could reset the isLoaded attribute on the
> other
> one - that should trigger a reload from the database when you call the
> accessor method.


Yeah, that's exactly what I'm looking for.  I'm not looking for it as a
special case, but as (I think) the "right" way to interpret the spec.  I'm
totally happy to be corrected on this--and the fact that all the providers
seem to NOT do this obviously makes me queasy.  Surely, I thought, I must be
missing something, but I can't find whatever it is that I'm missing.  :-)
That is, this seems like it should be the required behavior.


> Are you doing this in many places throughout your app, or is this a
> one-off?


Well, I'm doing it lots of places.  I don't have to, but it seemed to me
that forcing the relationship navigation machinery to load objects was more
elegant than "brute forcing" the loads by using refresh() or find().  That
is, I can surely take in what amounts to a giant object graph (E2 and E1 are
only parts of this giant hairball I have to work with) and break it apart
and perform EntityManager operations on all the component parts, but I'd
like to be able to force the JPA relationship navigation machinery to do as
much of that for me as possible.

The larger pattern here is that I'm mapping a legacy database that uses lots
of multiple primary keys that are strings (lots of "codes").  So a given
object might be related to another object in the database by three VARCHAR
fields.

Additionally, it is impractical for someone to build up an object graph of
the size that actually needs to be persisted.  It's just too big.  So rather
than forcing someone to create a graph with, in this case, an E2 and an E1
that they previously loaded, I want them to simply hand me an E2 with only
its "foreign keys" set--i.e. its "x" field in my example.  Everything
else--the relationship, the instantiation of the related entity--is
derivable from that.  I thought I had hit on a good pattern here, but it
kind of broke down when my reading of the spec. did.  :-)

Best,
Laird

-- 
View this message in context: http://n2.nabble.com/insertable-updatable-%3D-false-question-tp3439111p3446350.html
Sent from the OpenJPA Users mailing list archive at Nabble.com.

Re: insertable/updatable = false question

Posted by Michael Dick <mi...@gmail.com>.
Hi Laird,
I haven't tested any of this out, but I'd guess that OpenJPA (and apparently
other providers) have loaded your @ManyToOne relationship before you call
the accessor method. FetchMode.LAZY is a hint to the provider, not a mandate
(relevant section of the spec follows) :

Section 9.1.18, page 179 in my copy:

The EAGER strategy is a requirement on the persistence provider runtime that
data must be eagerlyfetched. The LAZY strategy is a hint to the persistence
provider runtime that data should be fetched
lazily when it is first accessed. The implementation is permitted to eagerly
fetch data for which the
LAZY strategy hint has been specified. In particular, lazy fetching might
only be available for Basic
mappings for which property-based access is used.


If the field was loaded by the provider there may be no reason to think that
it's dirty and we need to fetch it from the database (em.refresh will force
a reload).

It's possible that we could detect this case. Basically you have two fields
 (read-only and read-write) in an entity mapped to the same column. Once the
RW field has been updated we could reset the isLoaded attribute on the other
one - that should trigger a reload from the database when you call the
accessor method. I'd have to look at the relevant code a bit to make sure
but it sounds possible.

Are you doing this in many places throughout your app, or is this a
one-off?

-mike

On Fri, Aug 14, 2009 at 11:33 AM, ljnelson <lj...@gmail.com> wrote:

>
> On Fri, Aug 14, 2009 at 12:17 PM, Ravi P Palacherla (via Nabble) <
> ml-user+194843-327306094@n2.nabble.com<ml...@n2.nabble.com>
> <ml...@n2.nabble.com>
> >
> > wrote:
>
> > 2.2    Persistent Fields and Properties
> > If property-based access is used and lazy fetching is specified, portable
> > applications should not directly access the
> > entity state underlying the property methods of managed instances until
> > after it has been fetched by the persistence provider.
>
>
> Sure; I'm using field-based access, so this one doesn't apply.
>
>
> > 3.2.7.1    Merging Detached Entity State
> > The persistence provider must not merge fields marked LAZY that have not
> > been fetched: it must ignore such fields when merging.
> >
>
> Sure; this means that if I have an entity, E5, that has a lazy field to an
> E6 that has never been set, then if I merge E5 to E6, E5's null E6
> reference
> shouldn't be used to wipe out any E6-related state that might exist on
> disk.  That's not the issue here either.
>
> Thanks for dredging up these references, however.
>
> Best,
> Laird
>
> --
> View this message in context:
> http://n2.nabble.com/insertable-updatable-%3D-false-question-tp3439111p3446109.html
> Sent from the OpenJPA Users mailing list archive at Nabble.com.
>

Re: insertable/updatable = false question

Posted by ljnelson <lj...@gmail.com>.
On Fri, Aug 14, 2009 at 12:17 PM, Ravi P Palacherla (via Nabble) <
ml-user+194843-327306094@n2.nabble.com<ml...@n2.nabble.com>
> wrote:

> 2.2    Persistent Fields and Properties
> If property-based access is used and lazy fetching is specified, portable
> applications should not directly access the
> entity state underlying the property methods of managed instances until
> after it has been fetched by the persistence provider.


Sure; I'm using field-based access, so this one doesn't apply.


> 3.2.7.1    Merging Detached Entity State
> The persistence provider must not merge fields marked LAZY that have not
> been fetched: it must ignore such fields when merging.
>

Sure; this means that if I have an entity, E5, that has a lazy field to an
E6 that has never been set, then if I merge E5 to E6, E5's null E6 reference
shouldn't be used to wipe out any E6-related state that might exist on
disk.  That's not the issue here either.

Thanks for dredging up these references, however.

Best,
Laird

-- 
View this message in context: http://n2.nabble.com/insertable-updatable-%3D-false-question-tp3439111p3446109.html
Sent from the OpenJPA Users mailing list archive at Nabble.com.

RE: insertable/updatable = false question

Posted by Ravi Palacherla <ra...@oracle.com>.
Hi Laird,

I am not an expert in interpreting the spec , but I think the following points from the spec may  help

As per JSR 317 

2.2    Persistent Fields and Properties
If property-based access is used and lazy fetching is specified, portable applications should not directly access the 
entity state underlying the property methods of managed instances until after it has been fetched by the persistence provider.

3.2.7.1    Merging Detached Entity State
The persistence provider must not merge fields marked LAZY that have not been fetched: it must ignore such fields when merging.

Regards,
Ravi.

-----Original Message-----
From: ljnelson [mailto:ljnelson@gmail.com] 
Sent: Friday, August 14, 2009 7:47 AM
To: users@openjpa.apache.org
Subject: Re: insertable/updatable = false question


On Thu, Aug 13, 2009 at 7:46 PM, Ravi P Palacherla (via Nabble) <
ml-user+194843-327306094@n2.nabble.com<ml...@n2.nabble.com>
> wrote:

> I think I found a better solution.
>

Oh, there are plenty of solutions, yes, but I'm more concerned with what is
the intended behavior.  It is indeed true that if I do a refresh I will
reload the entity from the database.  But I can't see from the spec why I
should have to do this.  I would think that the lazy loading machinery would
effectively do this for me (in the case of the e2->e1 relationship).  I
don't see any place in the specification that tells me in this very common
scenario why I need to jump through extra hoops.

Thanks,
Laird

-- 
View this message in context: http://n2.nabble.com/insertable-updatable-%3D-false-question-tp3439111p3445160.html
Sent from the OpenJPA Users mailing list archive at Nabble.com.

Re: insertable/updatable = false question

Posted by ljnelson <lj...@gmail.com>.
On Thu, Aug 13, 2009 at 7:46 PM, Ravi P Palacherla (via Nabble) <
ml-user+194843-327306094@n2.nabble.com<ml...@n2.nabble.com>
> wrote:

> I think I found a better solution.
>

Oh, there are plenty of solutions, yes, but I'm more concerned with what is
the intended behavior.  It is indeed true that if I do a refresh I will
reload the entity from the database.  But I can't see from the spec why I
should have to do this.  I would think that the lazy loading machinery would
effectively do this for me (in the case of the e2->e1 relationship).  I
don't see any place in the specification that tells me in this very common
scenario why I need to jump through extra hoops.

Thanks,
Laird

-- 
View this message in context: http://n2.nabble.com/insertable-updatable-%3D-false-question-tp3439111p3445160.html
Sent from the OpenJPA Users mailing list archive at Nabble.com.

Re: insertable/updatable = false question

Posted by Ravi P Palacherla <ra...@oracle.com>.
Hi,

I think I found a better solution.

after flush() 
call em.refresh(e2)
This will refresh the state of e2 from the datastore. 

now e2.gete1() should return NON-NULL value.

Previously, it was not working because even though flush() updates the
datastore with values of e2.
The next call e2.gete1() is not going to datastore but using the e2's state
in the persistenceContext.

Please try and let me know if it has any issues.

Regards,
Ravi.


ljnelson wrote:
> 
> On Thu, Aug 13, 2009 at 4:39 PM, Ravi P Palacherla (via Nabble) <
> ml-user+194843-327306094@n2.nabble.com<ml...@n2.nabble.com>
>> wrote:
> 
>> Hi Laird,
>>
>> I also think that you should get a NON-NULL for e2.gete1() after flush,
>> provided e2 is managed at the time you are doing e2.gete1().
>>
> 
> Interestingly enough, I have run this using all the persistence providers,
> and they all give me NULL.  I just can't see reading the spec that way,
> but
> everyone has, I guess.
> 
> Let me try your suggestion.
> 
> Thanks,
> Laird
> 
> 

-- 
View this message in context: http://n2.nabble.com/insertable-updatable-%3D-false-question-tp3439111p3441686.html
Sent from the OpenJPA Users mailing list archive at Nabble.com.

Re: insertable/updatable = false question

Posted by ljnelson <lj...@gmail.com>.
On Thu, Aug 13, 2009 at 4:39 PM, Ravi P Palacherla (via Nabble) <
ml-user+194843-327306094@n2.nabble.com<ml...@n2.nabble.com>
> wrote:

> Hi Laird,
>
> I also think that you should get a NON-NULL for e2.gete1() after flush,
> provided e2 is managed at the time you are doing e2.gete1().
>

Interestingly enough, I have run this using all the persistence providers,
and they all give me NULL.  I just can't see reading the spec that way, but
everyone has, I guess.

Let me try your suggestion.

Thanks,
Laird

-- 
View this message in context: http://n2.nabble.com/insertable-updatable-%3D-false-question-tp3439111p3440703.html
Sent from the OpenJPA Users mailing list archive at Nabble.com.

RE: insertable/updatable = false question

Posted by Ravi Palacherla <ra...@oracle.com>.
Hi Laird,

I also think that you should get a NON-NULL for e2.gete1() after flush, provided e2 is managed at the time you are doing e2.gete1().

What if you do 
e2 = em.find(e2.class,PK);
e2.gete1();

then are you getting a non-null e1 value ?

Regards,
Ravi.

-----Original Message-----
From: Laird Nelson [mailto:ljnelson@gmail.com] 
Sent: Thursday, August 13, 2009 10:33 AM
To: users@openjpa.apache.org
Subject: insertable/updatable = false question

I have an entity, E2, that contains a many-to-one relationship with E1 like
this:

// assume E1's primary key column is x
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "x", referencedColumnName = "x", insertable = false,
updatable = false)
private E1 e1;

Then in E2 I also have a field like this:

@Column(name = "x")
private int x;

When I first create an instance of E2, I set x to, say, 5.  Let's assume we
know that E1 exists in the database under a primary key of x = 5 as well.

If I examine the unpersisted/transient E2 instance, I can see that x indeed
is 5, and e1 is null, as I'd expect.

Now--we're on the server side for all this--I do this:

e2 = this.em.merge(e2);
this.em.flush();

After the flush, I would expect that:
e2 would be persisted
e2 would have its x = 5 (unchanged)
e2.e1 would be NON NULL.  That is if I did: e2.getE1(); I'd get back a
non-null return value.

But instead I am seeing that e2.e1 is null.

Is this expected behavior?

What I'm looking for is the ability for someone to create an E2 without an
E1 in hand, pass it in to my EJB method, where I will--using lazy
loading--"inflate" the E1 relationship.  I can't figure out how to make this
happen.

Thanks,
Laird

Re: insertable/updatable = false question

Posted by ljnelson <lj...@gmail.com>.
On Thu, Aug 13, 2009 at 1:10 PM, crispyoz (via Nabble) <
ml-user+243560-833017765@n2.nabble.com<ml...@n2.nabble.com>
> wrote:

> I didn't quite get what you were trying to describe


Let me try again.

I have multiple mappings for the same column.  Of those multiple mappings,
only one may be mutable.

So E2 has a @Basic *mutable* mapping for its "x" column and its "x"
field--you can set the "x" field as many times as you like to whatever value
you like (but read on).  At insert or update time, your changes will be
persisted.  Although I don't explicitly specify it, it is insertable = true
and updatable = true.

Then E2 has a lazy @ManyToOne mapping for its "e1" *field* whose @JoinColumn
is an *immutable* mapping to the "x" column.  That is, you could, I suppose,
set the e1 field to an E1 of your choice, if you wanted, but since that
mapping is marked as "insertable = false, updatable = false", the E1 you
install in this manner will be ignored at insert and at update time.  So if
you stuck an E1 in there, then saved the E2, your E1 would not be persisted.

It should follow from this--at least according to my reading--that I should
be able to set the "x" field to a value that is the primary key of an E1,
and set the "e1" field to null.  So let's say I set e2.x to 5, and let's say
that there's an E1 in the database with a PK of 5.

It should be the case that if I merge e2, I should now be able to say
e2.getE1(), and I should get back an E1 as computed by the JPA relationship
machinery.

So, in other words, it should be possible to create an E2 with, say, a value
for its "x" field of 5, and then, after a merge and perhaps a flush, you
SHOULD be able to say to that E2, "OK, give me your related E1, even though
I never specified it", and because you've supplied the raw materials for the
mapping, it should be able to lazily give you that E1 reference.

But I am not seeing this behavior.  I am unclear as to whether it is a bug
or a part of the specification that I am reading badly (it sure wouldn't be
the first time :-\).

Thanks,
Laird

-- 
View this message in context: http://n2.nabble.com/insertable-updatable-%3D-false-question-tp3439111p3439449.html
Sent from the OpenJPA Users mailing list archive at Nabble.com.

RE: insertable/updatable = false question

Posted by C N Davies <cn...@cndavies.com>.
I didn't quite get what you were trying to describe but in my case I have E1
and E2 joined as many to many. In any case that I make a change to E1 I only
want it remained linked to the E2 entity, not making any changes to any E2
entity since E2 is persisted as a stand-alone entity. 

In this case I set the field in E1 that joins to E2 as
(cascade={CascadeType.REFRESH})  since I never want E1 to change E2 only
maybe link to another E2 record. 

Hope I understood what you were saying correctly.

Regards

Chris

-----Original Message-----
From: Laird Nelson [mailto:ljnelson@gmail.com] 
Sent: Friday, 14 August 2009 2:33 AM
To: users@openjpa.apache.org
Subject: insertable/updatable = false question

I have an entity, E2, that contains a many-to-one relationship with E1 like
this:

// assume E1's primary key column is x
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "x", referencedColumnName = "x", insertable = false,
updatable = false)
private E1 e1;

Then in E2 I also have a field like this:

@Column(name = "x")
private int x;

When I first create an instance of E2, I set x to, say, 5.  Let's assume we
know that E1 exists in the database under a primary key of x = 5 as well.

If I examine the unpersisted/transient E2 instance, I can see that x indeed
is 5, and e1 is null, as I'd expect.

Now--we're on the server side for all this--I do this:

e2 = this.em.merge(e2);
this.em.flush();

After the flush, I would expect that:
e2 would be persisted
e2 would have its x = 5 (unchanged)
e2.e1 would be NON NULL.  That is if I did: e2.getE1(); I'd get back a
non-null return value.

But instead I am seeing that e2.e1 is null.

Is this expected behavior?

What I'm looking for is the ability for someone to create an E2 without an
E1 in hand, pass it in to my EJB method, where I will--using lazy
loading--"inflate" the E1 relationship.  I can't figure out how to make this
happen.

Thanks,
Laird