1

我正在尝试使用 AppEngine 和 JPA 持久性保存和加载带有日期的对象。但是,当我加载对象并尝试将其发送到 GWT 时,它会抛出此异常:

严重:javax.servlet.ServletContext 日志:调度传入 RPC 调用 com.google.gwt.user.client.rpc.SerializationException 时出现异常:类型“org.datanucleus.store.types.sco.simple.Date”未包含在无法加载可由此 SerializationPolicy 或其 Class 对象序列化的类型集。出于安全考虑,此类型不会被序列化。:实例 = Wed Oct 09 22:47:22 EDT 2013

这是我的服务器查询:

        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;

这是一些日志:

Oct 09, 2013 10:47:43 PM com.onixnet.sdm.server.SDMServiceImpl getSentItems
INFO: chloe@domain.com, Wed Oct 09 22:47:21 EDT 2013

这是有问题的班级:

package com.onixnet.sdm.shared;
import java.util.Date;
@Entity
public class EmailEvent implements Serializable {
...
private Date sendDate;

我已经尝试过 java.util.Date 和 java.sql.Date。我尝试在 AppEngine 管理界面中删除实体的所有行。两次它都会正确保存并加载,但不会序列化。(另一个错误是 'org.datanucleus.store.types.sco.simple.SqlDate' 无法序列化。当它是 java.sql.Date 时,它​​作为 long 保存到 DataStore 中。)

AppEngine SDK v1.8.5、Java 7、GWT 2.5.1。

4

1 回答 1

2

找到它:http ://bpossolo.blogspot.com/2013/03/upgrading-gae-app-from-jpa1-to-jpa2.html

增强的日期属性

我首先要说这种差异非常烦人。

在 JPA1 中,具有 java.util.Date 属性的实体按预期运行。您可以使用 new Date() 设置属性,持久化实体,正常加载它,并且该属性始终是真正的 java.util.Date。

在 JPA2 中,这发生了变化。现在,当具有 java.util.Date 属性的实体持久保存到数据存储区时,datanucleus 将 Date 实例替换为 org.datanucleus.store.types.sco.simple.Date。

问题是 org.datanucleus.store.types.sco.simple.Date 不能通过 GWT-RPC 序列化。这意味着在 GWT-RPC 序列化发生之前,必须从 EntityManager 的持久性上下文中分离从数据存储中检索到的具有 Date 属性的任何实体。在分离期间,所有那些特殊的 Date 对象都被转换回 java.util.Date 对象。

有几种方法可以做到这一点。

第一个选项是通过调用显式分离持久对象

entityManager.detach(obj)。

还有一种方法可以将分离级联到子实体,尽管您可能不需要担心这一点,因为您的 GAE 数据存储实体可能没有嵌套对象图。

第二个选项是设置一个 datanucleus 属性,该属性在 EntityManager 关闭时自动分离所有对象。如果您可以确保在 GWT-RPC 序列化发生之前关闭您的 EntityManager,建议您这样做。在您的 persistence.xml 文件中,添加

True 是默认行为,但最好是明确的。

如果您使用的是“open-session-in-view”样式模式(您在 servlet 过滤器中创建 EntityManager 并将其存储在线程局部变量中以在 http 请求期间使用),则 'detach-on-close ' 将不起作用,因为 GWT-RPC 序列化将在您的 EntityManager 关闭之前发生。

第三个选项是设置一个数据核属性,在提交时自动分离所有对象。如果您使用“open-session-in-view”模式,这很好。在您的 persistence.xml 文件中,添加

您可以在此处阅读有关分离的更多信息。小心泄露数据核日期

想象以下场景。

@Entity public class User { @Id Long key; 日期会员自;}

公共类 ProfileViewDTO { 日期 userJoinDate; }

//在 GWT-RPC servlet 中 User user = (User)entityManager.find(User.class, userKey);

ProfileViewDTO dto = new ProfileViewDTO(); dto.userJoinDate = user.memberSince; //日期泄漏!

entityManager.detach(user); entityManager.close();

返回 dto; //发生序列化错误!

可以看到,datanucleus 日期对象已经泄露,会导致序列化错误。为避免这种情况,请在设置 dto 的属性之前分离用户或显式复制日期。在 GAE 内存缓存中缓存对象之前执行此操作也很重要。

我到处都有“日期泄漏”,这就是为什么我发现这种新的 JPA2 行为真的很烦人。我花了一段时间来追踪它们并修复它们。

我用了

for (EmailEvent row: al) {
        em.detach(row);
        log.info(row.getSenderEmail() + ", " + row.getSendDate());
}

我还设置了属性(但这似乎没有帮助)。

于 2013-10-10T03:25:40.823 回答