Reputation: 26264
I'm trying to save & load an object with a Date using AppEngine and JPA persistence. However, when I load the object and try to send it to GWT, it throws this exception:
SEVERE: javax.servlet.ServletContext log: Exception while dispatching incoming RPC call com.google.gwt.user.client.rpc.SerializationException: Type 'org.datanucleus.store.types.sco.simple.Date' was not included in the set of types which can be serialized by this SerializationPolicy or its Class object could not be loaded. For security purposes, this type will not be serialized.: instance = Wed Oct 09 22:47:22 EDT 2013
This is my server query:
Query q = em.createQuery("select ee from EmailEvent ee");
al.addAll( q.getResultList() );
for (EmailEvent row: al) {
log.info(row.getSenderEmail() + ", " + row.getSendDate());
}
return al;
This is some of the log:
Oct 09, 2013 10:47:43 PM com.onixnet.sdm.server.SDMServiceImpl getSentItems
INFO: [email protected], Wed Oct 09 22:47:21 EDT 2013
This is the class with the problem:
package com.onixnet.sdm.shared;
import java.util.Date;
@Entity
public class EmailEvent implements Serializable {
...
private Date sendDate;
I've tried both java.util.Date and java.sql.Date. I've tried deleting all rows for the entity in the AppEngine admin interface. Both times it will save correctly, and load, but it won't serialize. (The other error was 'org.datanucleus.store.types.sco.simple.SqlDate' could not be serialized. When it was java.sql.Date it saved into the DataStore as a long.)
AppEngine SDK v1.8.5, Java 7, GWT 2.5.1.
Upvotes: 2
Views: 757
Reputation: 26264
Found it: http://bpossolo.blogspot.com/2013/03/upgrading-gae-app-from-jpa1-to-jpa2.html
Enhanced Date properties
I'll start by saying this difference turned out to be extremely annoying.
In JPA1, an entity with a java.util.Date property functioned just as one would expect. You could set the property with new Date(), persist the entity, load it normally and the property was always a true java.util.Date.
In JPA2 this changed. Now, when an entity with a java.util.Date property is persisted to the datastore, datanucleus replaces the Date instance with a org.datanucleus.store.types.sco.simple.Date.
The problem is that the org.datanucleus.store.types.sco.simple.Date cannot be serialized over GWT-RPC. That means any entity with a Date property which has been retrieved from the datastore must be detached from the EntityManager's persistence context before GWT-RPC serialization occurs. During detachment, all those special Date objects are converted back into java.util.Date objects.
There are several ways to do this.
The first option is to explicitly detach a persistent object by calling
entityManager.detach(obj).
There is also a way to cascade detachment to child entities although you probably won't need to do worry about this since your GAE datastore entities probably won't have a nested object graph.
The second option is to set a datanucleus property that automatically detaches all objects when the EntityManager is closed. This is recommended if you can ensure that your EntityManagers are closed before GWT-RPC serialization occurs. In your persistence.xml file, add
True is the default behaviour but it's good to be explicit.
If you are using an "open-session-in-view" style pattern (where you create the EntityManager in a servlet filter and store it in a thread-local variable for use during the http request), then 'detach-on-close' will not work because GWT-RPC serialization will occur before your EntityManager is closed.
The third option is to set a data nucleus property that automatically detaches all objects on commit. This is good if you are using a "open-session-in-view" pattern. In your persistence.xml file, add
You can read more about detachment here. Beware of leaking datanucleus dates
Imagine the following scenario.
@Entity public class User { @Id Long key; Date memberSince; }
public class ProfileViewDTO { Date userJoinDate; }
//in GWT-RPC servlet User user = (User)entityManager.find(User.class, userKey);
ProfileViewDTO dto = new ProfileViewDTO(); dto.userJoinDate = user.memberSince; //date leak!
entityManager.detach(user); entityManager.close();
return dto; //serialization error occurs!
As you can see, the datanucleus date object has leaked and will cause a serialization error. To avoid this, either detach the user before setting the dto's property or copy the date explicitly. It is also important to do this before caching an object in GAE memcache.
I had "date leaks" all over which is why I found this new JPA2 behaviour really annoying. It took me a while to track them all down and fix them.
I used
for (EmailEvent row: al) {
em.detach(row);
log.info(row.getSenderEmail() + ", " + row.getSendDate());
}
and I also set the property (but that didn't seem to help).
Upvotes: 3