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.