openjpa-users mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Craig L Russell <Craig.Russ...@Sun.COM>
Subject Re: Does OpenJPA replace Collections?
Date Mon, 13 Apr 2009 23:18:07 GMT
Hi Paul,

On Apr 13, 2009, at 9:04 AM, Paul Copeland wrote:

> Are there any responses from the OpenJPA experts on my two  
> assertions below?  If the assertions seem wrong I will put together  
> examples that demonstrate the behavior.  If the assertions are  
> correct that is not necessary.
>
> From JPA Spec Section 2.1.7 - "If there are no associated entities  
> for a multi-valued relationship of an entity fetched from the  
> database,
> the persistence provider is responsible for returning an empty  
> collection as the value of the relationship."
>
> Note the words "fetched from the database". My reading of this is  
> that if the Entity is new and has not been flushed to the database  
> (even though persist() has been called) the value could be null  
> rather than an empty collection. So the behavior of OpenJPA  
> returning null (assertion #1) would be consistent with the spec.

That's how I read it as well. Until the new entity is flushed, there's  
no reason to have the entity manager provider messing with the values.
>
>
> - Paul
>
> On 4/9/2009 12:22 PM, Paul Copeland wrote:
>> Thanks for the assistance Craig -
>>
>> Here are two assertions that I have observed in my testing with  
>> OpenJPA 1.2.1 -
>>
>> (1) A Field Access persistent collection has a null value when the  
>> field is accessed if the collection is empty. This is the state of  
>> the field in the transaction after the entity is first persisted  
>> before the transaction is committed (these are the conditions that  
>> occur in my process).  Corollary - the null field is NOT  
>> automatically changed to an empty Collection when first accessed. A  
>> method returning the collection field will return null.

This is discussed above. The entity is not "fetched" but rather newly  
persisted.
>>
>>
>> (2) The value of a null collection field (state as in #1 above)  
>> that has been assigned to an initialized non-null value may be  
>> automatically replaced before the transaction is committed at which  
>> point references to the assigned value will be stale and no longer  
>> updated (for instance when entity's are added to the collection).

This is discussed above. Until flush, any user changes to the  
collection should be reflected in the database.

But one other thing to consider. It's the application's responsibility  
to manage both sides of a relationship to be consistent at commit. So  
if you're looking to update only the other side of a relationship  
you're in trouble unless you use some OpenJPA special techniques.

Craig
>>
>>
>> If the experts believe either of these assertions are incorrect  
>> then I definitely want to investigate further.
>>
>> - Paul
>>
>> (further comments below)
>>
>>
>> On 4/9/2009 11:13 AM, Craig L Russell wrote:
>>> Hi Paul,
>>>
>>> On Apr 9, 2009, at 9:40 AM, Paul Copeland wrote:
>>>
>>>> Couple of clarifications -
>>>>
>>>> A lazily loaded FIELD ACCESS collection is a null value when  
>>>> initially accessed if the Collection is EMPTY (I said "null"  
>>>> incorrectly below).
>>>
>>> My comment below was intended to compare your "if null then  
>>> initialize" paradigm with my "initialize to an empty collection  
>>> during construction". So if the first time you access the  
>>> collection it is null your code sets the value to an empty  
>>> collection. My recommended code would never encounter a null  
>>> collection.
>>
>> Your way works (as do other ways).  :-)
>>
>>>>
>>>> The test I have shows this behavior for a newly persisted Entity  
>>>> during the same transaction where em.persist(entity) is called.  
>>>> This is with a LAZY loaded collection.
>>>
>>> During persist, the provider should not replace fields. Replacing  
>>> fields behavior should happen at commit (flush) time. So if you  
>>> never explicitly initialize a field, it should have its Java  
>>> default value until flush.
>>
>> This is NOT what I am seeing.  In fact the replacement happens  
>> during the transaction under certain conditions where the proxy is  
>> apparently created during the transaction some time after the call  
>> to em.persist(entity) and before commit.
>>
>>>
>>> If you're talking about wrapping the persistent collection with an  
>>> unmodifiable collection then you're talking about adding more  
>>> objects. I thought you were trying to avoid any object construction?
>>
>> I would construct the unmodifiable collection (if the idiom worked)  
>> only if and when the value is accessed and has already been  
>> loaded.  Other things being equal, I don't want to construct tens  
>> of thousands of Collections in a tight loop that are never used.   
>> Given database latencies it is a small point in overall  
>> performance.  As I said, there are good arguments either way and  
>> your recommendation is one reasonable approach, but apparently not  
>> a JPA requirement.
>>
>>>
>>> In some applications there is a difference between an empty  
>>> collection and a null collection. There are properties that allow  
>>> that behavior to be implemented as well, although that's non- 
>>> standard and a bit more complicated.
>>>
>>> It might be easier to look at a test case because I think we're  
>>> talking past each other.
>>>
>>> Craig
>>>>
>>>>
>>>> On 4/9/2009 9:26 AM, Paul Copeland wrote:
>>>>> Hi Craig -
>>>>>
>>>>> My experience is not what you are describing.  A lazily loaded  
>>>>> FIELD ACCESS collection is a null value when initially accessed  
>>>>> if the Collection is null (possibly a PROPERTY ACCESS collection  
>>>>> behaves differently as mentioned by Pinaki , I haven't tested  
>>>>> that).
>>>>>
>>>>> To repeat what is below -
>>>>>
>>>>>                   getMyPcList()
>>>>>
>>>>> returns null if the Collection is empty unless you initialize  
>>>>> the value with "new ArrayList()".  This is what my testing shows  
>>>>> with 1.2.1 - I wish it weren't this way since that might it make  
>>>>> it possible to use the Collections.unmodifiedList() idiom (as it  
>>>>> is that idiom has unreliable behavior).   If the experts are  
>>>>> pretty sure that I am wrong about this then I definitely want to  
>>>>> investigate it further.  I'd like to hear more.
>>>>>
>>>>> I don't think you have given a reason to require initializing  
>>>>> the Collection at construction time or at first access -- there  
>>>>> are reasonable aesthetic and performance arguments either way.
>>>>>
>>>>> - Paul
>>>>>
>>>>>
>>>>> On 4/9/2009 7:01 AM, Craig L Russell wrote:
>>>>>> Hi Paul,
>>>>>>
>>>>>> I like to think of entities as POJOs first, so I can test them  
>>>>>> without requiring them to be persistent. So if you want code to 

>>>>>> be able to add elements to collections, the collections must  
>>>>>> not be null.
>>>>>>
>>>>>> If you construct the field as null and then "lazily"  
>>>>>> instantiate an empty collection, then anyway you end up with an 

>>>>>> empty collection the first time you access the field. And  
>>>>>> constructing an empty collection should not be even a blip on  
>>>>>> your performance metric.
>>>>>>
>>>>>> Considering everything, I still recommend that you instantiate  
>>>>>> an empty collection when you construct an entity.
>>>>>>
>>>>>> Craig
>>>>>>
>>>>>> On Apr 8, 2009, at 10:21 AM, Paul Copeland wrote:
>>>>>>
>>>>>>> Pinaki -
>>>>>>>
>>>>>>> I tried your suggestion of not initializing the value of  
>>>>>>> myPcList and I get a null pointer exception when adding to an
 
>>>>>>> empty list.
>>>>>>>
>>>>>>> I noticed your example was for Property access and Russell  
>>>>>>> (and I) were talking about Field access.  Do you agree that it
 
>>>>>>> is necessary to initialize an empty list when using Field  
>>>>>>> access?
>>>>>>>
>>>>>>> On Craig's advice to always construct a new ArrayList(), why
 
>>>>>>> is that necessary instead of just constructing it in the  
>>>>>>> getter when it tests to null?  Otherwise you are constructing
 
>>>>>>> an ArrayList that is unnecessary when the List is NOT empty 

>>>>>>> (usually) and also unnecessary in the case of LAZY loading if
 
>>>>>>> the List is never accessed (perhaps also a frequent case).  In
 
>>>>>>> some applications you might create lots of these objects and
 
>>>>>>> normal optimization is to avoid calling constructors  
>>>>>>> unnecessarily.  Just want to be clear about whether it is  
>>>>>>> necessary.
>>>>>>>
>>>>>>> - Paul
>>>>>>>
>>>>>>> On 4/8/2009 9:43 AM, Paul Copeland wrote:
>>>>>>>> Thanks Pinaki -
>>>>>>>>
>>>>>>>> I think you are saying that at some point the proxy object
 
>>>>>>>> does replace the local List.  Is that right?
>>>>>>>>
>>>>>>>> I have seen that model - if (myPcList == null) myPcList =
new  
>>>>>>>> ArrayList() - in various examples (not sure where now). 
 
>>>>>>>> Thanks for clearing that up.  But then Craig Russell  
>>>>>>>> contradicts you in his reply (below) where he recommends
 
>>>>>>>> always initializing the Collection in the constructor (which
 
>>>>>>>> seems like a performance anti-pattern of wasted constructor
 
>>>>>>>> calls since usually it will be replaced by the proxy).  
Are  
>>>>>>>> you and Craig saying opposite things here?
>>>>>>>>
>>>>>>>> In my testing when the List is empty - (myPcList == null)
-  
>>>>>>>> does indeed evaluate to true.
>>>>>>>>
>>>>>>>>            getMyPcList().add(new MyPcObject())
>>>>>>>>
>>>>>>>> Therefore I thought the above would cause a null pointer
 
>>>>>>>> exception when the List is empty.  You say that won't happen
 
>>>>>>>> so I'll give it a try!
>>>>>>>>
>>>>>>>> - Paul
>>>>>>>>
>>>>>>>>
>>>>>>>> On 4/8/2009 3:16 AM, Pinaki Poddar wrote:
>>>>>>>>> Hi,
>>>>>>>>> According to JPA spec:
>>>>>>>>> "If there are no associated entities for a multi-valued
 
>>>>>>>>> relationship of an entity fetched from the database,
>>>>>>>>> the persistence provider is responsible for returning
an  
>>>>>>>>> empty collection as the value of the relationship."
>>>>>>>>>
>>>>>>>>> That is what OpenJPA does. So the application do not
need to  
>>>>>>>>> return an empty list for a null (initialized) list.
>>>>>>>>>
>>>>>>>>> OpenJPA proxies all returned collections. So application
 
>>>>>>>>> code can simply do the following
>>>>>>>>>
>>>>>>>>> // In the domain class
>>>>>>>>> private List<MyPcObject> myPcList = null; // never
explictly  
>>>>>>>>> initialized
>>>>>>>>>
>>>>>>>>> @OneToMany (mappedBy="ownerSide", fetch=FetchType.LAZY,
  
>>>>>>>>> cascade=CascadeType.PERSIST)
>>>>>>>>> public  List<Promotion> getMyPcList()  {
>>>>>>>>>    return myPcList; // return as it is
>>>>>>>>> }
>>>>>>>>>
>>>>>>>>> // In the application
>>>>>>>>> List<Promotion> list = owner.getMyPcList();
>>>>>>>>> assertNotNull(list);
>>>>>>>>> assertTrue(java.util.List.class.isInstance(list));
>>>>>>>>> assertNotSame(java.util.ArrayList.class, list.getClass());
>>>>>>>>> list.add(new MyPcObject());
>>>>>>>>> owner.setMyPcList(list);
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> On Apr 7, 2009, at 11:10 PM, Paul Copeland wrote:
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>> Can OpenJPA replace a Collection when it is loaded?
>>>>>>>>>>
>>>>>>>>>> With the code below when the list is initially empty
you  
>>>>>>>>>> need to  create a List (ArrayList) so you can add
elements  
>>>>>>>>>> to it. When I  persisted new objects on the ManyToOne
side  
>>>>>>>>>> and added them to the  List that worked.  But the
first  
>>>>>>>>>> time the List was loaded it seemed  to replace my
ArrayList  
>>>>>>>>>> with the newly loaded data and made an older  reference
to  
>>>>>>>>>> the ArrayList stale (no longer updated when more
 elements  
>>>>>>>>>> were added to myPcList).  This was all in one transaction.
>>>>>>>>>>
>>>>>>>>>> So now I wonder if the initial null List is a special
case  
>>>>>>>>>> or if  OpenJPA might replace the Collection anytime
it  
>>>>>>>>>> decides to load it  again.  Anyone know the answer?
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>> If the list is persistent and the class is enhanced,
the  
>>>>>>>>> collection  will always reflect what's in the database.
>>>>>>>>>
>>>>>>>>>> If I don't create an initial ArrayList how can I
add  
>>>>>>>>>> elements when  the List is empty?
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>> I'd recommend always having a non-empty list. Initialize
it  
>>>>>>>>> in the  constructor to an empty list and don't check
it  
>>>>>>>>> after that.
>>>>>>>>>
>>>>>>>>> Here's what it would look like:
>>>>>>>>>
>>>>>>>>>> @OneToMany (mappedBy="ownerSide", fetch=FetchType.LAZY,
  
>>>>>>>>>> cascade=CascadeType.PERSIST)
>>>>>>>>>> private List<MyPcObject> myPcList = new  
>>>>>>>>>> ArrayList<MyPcObject>();
>>>>>>>>>>
>>>>>>>>>> List<Promotion> getMyPcList()
>>>>>>>>>> {
>>>>>>>>>>   return myPcList;
>>>>>>>>>> }
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>> Craig
>>>>>>>>> Craig L Russell
>>>>>>>>> Architect, Sun Java Enterprise System http://db.apache.org/jdo
>>>>>>>>> 408 276-5638 mailto:Craig.Russell@sun.com
>>>>>>>>> P.S. A good JDO? O, Gasp!
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> -----
>>>>>>>>> Pinaki Poddar                      http:// 
>>>>>>>>> ppoddar.blogspot.com/
>>>>>>>>>                                    http://www.linkedin.com/in/pinakipoddar
>>>>>>>>> OpenJPA PMC Member/Committer
>>>>>>>>> JPA Expert Group Member
>>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>
>>>>>>
>>>>>> Craig L Russell
>>>>>> Architect, Sun Java Enterprise System http://db.apache.org/jdo
>>>>>> 408 276-5638 mailto:Craig.Russell@sun.com
>>>>>> P.S. A good JDO? O, Gasp!
>>>>>>
>>>>>
>>>>>
>>>>>
>>>>
>>>
>>> Craig L Russell
>>> Architect, Sun Java Enterprise System http://db.apache.org/jdo
>>> 408 276-5638 mailto:Craig.Russell@sun.com
>>> P.S. A good JDO? O, Gasp!
>>>
>>
>>
>>
>

Craig L Russell
Architect, Sun Java Enterprise System http://db.apache.org/jdo
408 276-5638 mailto:Craig.Russell@sun.com
P.S. A good JDO? O, Gasp!


Mime
View raw message