Some ORMs may return a different type when a persisted Collection is re-loaded. But if you modify the original Collection after a flush() that could be problematic. In any case the user needs to know what behavior can be relied upon (specification).

Per the JPA spec, Date, Time, Calendar, etc. are a special case of mutable persistent fields, but only entities are required to be handled in persistent Collections. Apparently the OpenJPA @PersistentCollection has no restrictions on the type of elements, mutable or immutable, that Collections can contain.

On 4/2/2009 7:49 AM, Tedman Leung wrote:
that is correct, if I leave out the fetch type, it defaults to lazy and it works fine (so long as the entity is in an attached state).

The other observation I noticed is that accessing the collection does not actually materialise the variable. It returns the data while I'm in the transaction / while it's attached, but as soon as it is detached I no longer have access to the data even if it were previously accessed. This prevented me from hacking a work around with @PostLoad and just calling collection.size() in it.

As for your second question, my answer is, I don't know. However I know in
some other ORM's, in the past, they wrote their own wrappers around things
like Sets and Map's so they could track additions and removals to the set.
Since this pertains to primitives only, almost all persistable primitives
are immutable (that I can think of) so it would generally suffice for
catching modifications.


On Thu, Apr 02, 2009 at 07:26:38AM -0700, Paul Copeland wrote:
Hi Ted -

Just to clarify, have you tried this with FetchType.EAGER and with no FetchType specified and in those cases the problem does not happen?

A more general observation is that the semantics of this OpenJPA @PersistentCollection extension do not seem to be fully specified (is there a document that ties all the OpenJPA extensions together in a cohesive specification?). In this case the elements of the Set are not persistence capable objects, they cannot be enhanced, and they do not have Identity or version information. So how would the EntityManager know if an element is added to, or removed from, the Set, or if a member of the Set has a field changed? There is not even a way to invoke Persist or Remove on a member of the Set since they are not Entity objects. Possibly the only reliable strategy would be for the EntityManager to always delete all the members of the Set and re-insert them every time the Set is loaded. That could be very expensive if the Set is large or if there a large number of these Set objects that are frequently loaded. Or does OpenJPA wrap these non-entity persistent elements into some kind of runtime pseudo-Entity (also expensive and might not resolve all of these issues)?

- Paul


On 4/1/2009 5:49 PM, Tedman Leung wrote:
So I have a more clear picture of this error now. Quite simply eager fetching of a persistent collection of strings fails.

as an example :

        @PersistentCollection(fetch=FetchType.EAGER)
        private HashSet<String> testStrings=new HashSet<String>();

Just create the entity, add a string to the collection, the merge / persist it.

Then clear the entityManager and any second level caches you have (or just stop the jvm), and load the entity back and I get <openjpa-1.2.1-r752877:753278 nonfatal general error> org.apache.openjpa.persistence.PersistenceException: java.lang.String cannot be cast to org.apache.openjpa.enhance.PersistenceCapable
                at 
org.apache.openjpa.kernel.BrokerImpl.find(BrokerImpl.java:875)
                at 
org.apache.openjpa.kernel.BrokerImpl.find(BrokerImpl.java:769)
                at 
org.apache.openjpa.kernel.DelegatingBroker.find(DelegatingBroker.java:183)
                at 
org.apache.openjpa.persistence.EntityManagerImpl.find(EntityManagerImpl.java:452)
                at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
                at 
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
                at 
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
                at java.lang.reflect.Method.invoke(Method.java:597)
        ...
        Caused by: java.lang.ClassCastException: java.lang.String cannot be 
cast to org.apache.openjpa.enhance.PersistenceCapable
                at 
org.apache.openjpa.jdbc.kernel.JDBCStoreManager.setInverseRelation(JDBCStoreManager.java:408)
                at 
org.apache.openjpa.jdbc.kernel.JDBCStoreManager.initializeState(JDBCStoreManager.java:380)
                at 
org.apache.openjpa.jdbc.kernel.JDBCStoreManager.initialize(JDBCStoreManager.java:278)
                at 
org.apache.openjpa.kernel.DelegatingStoreManager.initialize(DelegatingStoreManager.java:111)
                at 
org.apache.openjpa.kernel.ROPStoreManager.initialize(ROPStoreManager.java:57)
                at 
org.apache.openjpa.kernel.BrokerImpl.initialize(BrokerImpl.java:894)
                at 
org.apache.openjpa.kernel.BrokerImpl.find(BrokerImpl.java:852)

 Note that you must not be reloading the entity from memory, i.e.
you must clear the entity manager and make sure the entity cache is
off, or restart the jvm.
If you don't turn the entity caches off, it will just give you
back what you peristed and it will look like it works.

Anyone got any ideas on how to fix or work around this? I'd really prefer my entity detached but I want this data
fetched eagerly.


Reply via email to