繁体   English   中英

何时以及为什么 JPA 实体应该实现 Serializable 接口?

[英]When and why JPA entities should implement the Serializable interface?

问题在标题中。 下面我只是描述了我的一些想法和发现。

当我有一个非常简单的域模型(3 个没有任何关系的表)时,我所有的实体都没有实现Serializable接口。

但是当域模型变得更加复杂时,我得到了一个RuntimeException ,说我的一个实体没有实现Serializable

我使用 Hibernate 作为 JPA 实现,我想知道:

  1. 它是供应商特定的要求/行为吗?
  2. 我的可序列化实体会发生什么? 它们应该可序列化以进行存储还是传输?
  3. 什么时候有必要使我的实体可序列化?

根据 JPA 规范:

如果实体实例要作为分离对象按值传递(例如,通过远程接口),则实体类必须实现 Serializable 接口。

“JSR 220:Enterprise JavaBeansTM,3.0 版 Java Persistence API 3.0 版,2006 年 5 月 2 日最终发布”

如果您需要通过网络传输实体(将它们序列化为其他表示形式)、将它们存储在 http 会话中(然后由 servlet 容器将其序列化到硬盘)等,则需要您的实体是可Serializable

只是为了持久性,不需要Serializable ,至少在 Hibernate 中是这样。 但最好的做法是让它们Serializable

如果您混合使用 HQL 和本机 SQL 查询,通常会发生这种情况。 在 HQL 中,Hibernate 将您传入的类型映射到 DB 理解的任何类型。 当您运行本机 SQL 时,您必须自己进行映射。 如果不这样做,则默认映射是序列化参数并将其发送到数据库(希望它确实理解它)。

JPA规范

根据 JPA 规范,只有当实体需要从一个 JVM 传递到另一个 JVM 或者实体被需要由 EJB 容器钝化的有状态会话 Bean 使用时,才应该实现可Serializable

如果实体实例要作为分离对象按值传递(例如,通过远程接口),则实体类必须实现Serializable接口。

休眠

Hibernate 只要求实体属性是Serializable ,而不是实体本身。

然而,实现 JPA 规范,所有关于Serializable实体的 JPA 要求也适用于 Hibernate。

雄猫

根据Tomcat 文档HttpSession属性也需要是Serializable

每当 Apache Tomcat 正常关闭并重新启动时,或者触发应用程序重新加载时,标准管理器实现将尝试将所有当前活动的会话序列化到通过路径名属性定位的磁盘文件。 当应用程序重新加载完成时,所有这些保存的会话将被反序列化和激活(假设它们同时没有过期)。

为了成功恢复会话属性的状态,所有这些属性必须实现 java.io.Serializable 接口。

因此,如果实体存储在HttpSession ,则它应该实现Serializable

根据hibernate docs ,在使用 @JoinColumn 注释时:

它还有一个名为referencedColumnName参数。 此参数声明目标实体中将用于连接的列。 请注意,当对非主键列使用referencedColumnName时,关联的类必须是Serializable

如果我们只谈持久化, Serializable是不需要的,但最好的做法是让实体Serializable

如果我们将domain / entities对象直接暴露给表示层,而不是使用DTO ,那么我们需要实现Serializable 这些域对象可以存储在HTTPSession以用于缓存/优化目的。 http-session 可以被序列化或集群。 并且在JVM实例之间传输数据也需要它。

当我们使用DTO来解耦持久层和服务层时,将域对象标记为Serializable会适得其反,并且会违反“ encapsulation ”。 然后它变成了一种反模式。

复合标识符

主键类必须是可序列化的。

POJO 模型

如果将实体实例作为分离对象远程使用,则实体类必须实现Serializable接口。

缓存
此外,如果您正在实施clustered二级cache那么您的实体必须是可serializable 标识符必须是可Serializable因为这是 JPA 要求,因为identifier可能用作二级缓存条目的键。

当我们序列化实体时,请确保提供带有私有访问修饰符的显式serialVersionUID 因为如果可serializable类未显式声明serialVersionUID ,那么序列化运行时将根据该类的各个方面计算该类的默认serialVersionUID值,如 Java(TM) 对象序列化规范 中所述。 默认的serialVersionUID计算对可能因编译器实现而异的类详细信息高度敏感,因此可能会在反序列化期间导致意外的InvalidClassExceptions

为了补充提到 JSR-317 规范的 Conor 的好回答。 通常,EAR 项目由一个 EJB 模块和通过远程接口公开的 EJB 组成。 在这种情况下,您需要使实体 bean 可序列化,因为它们在远程 EJB 中聚合并构建为通过网络连接。

没有 CDI 的 JEE6 战争项目:可以包含由不可序列化的 JPA 实体支持的 EJB lite。

带有 CDI 的 JEE6 战争项目:使用会话、应用程序或对话范围的 Bean 必须可序列化,但使用请求范围的 Bean 不必可序列化。 因此,底层 JPA 实体 bean - 如果有的话 - 将遵循相同的语义。

我相信您的问题与具有未注释的复杂类型(类)的字段有关。 在这种情况下,默认处理将以序列化形式将对象存储在数据库中(这可能不是您想要做的)示例:

Class CustomerData {
    int getAge();
    void setAge(int age);
}

@Entity
Class Customer {
  CustomerData getCustomerData();
  void setCustomerData(CustomerData data)
}

在上述情况下,CustomerData 将以其序列化形式保存在数据库中的字节数组字段中。

请参阅http://www.adam-bien.com/roller/abien/entry/do_jpa_entities_have_to它说,java.io.Serializable 的实现只是在 JVM 实例之间通过 IIOP 或 JRMP (RMI) 传输数据所必需的。 在纯 Web 应用程序的情况下,域对象有时存储在 HTTPSession 中以用于缓存/优化目的。 http 会话可以序列化(钝化)或集群。 在这两种情况下,所有内容都必须是可序列化的。

如果要序列化类,则必须实现 Serializable。 这与 JPA 没有直接关系,并且 JPA 规范不要求实体是可序列化的。 如果 Hibernate 真的抱怨这个,我想这是一个 Hibernate 错误,但我想你直接或间接地对实体做了一些其他的事情,这需要它们是可序列化的。

  1. 什么时候有必要使我的实体可序列化?

使用 diskstore 作为二级缓存实现 ehcache(即在实体或存储库/服务方法上使用@Cacheable注释)需要 Serializable,否则缓存将失败( NotSerializableException )将实体写入磁盘缓存。

使用 postman 或 ajax 或 angular js 等远程命中.....,可能会导致与 Jackson fastxml 的 StackOverflow 异常的重复循环。所以,最好使用序列化程序。

当 JPA 实体被远程 EJB 操作用作参数或返回值时

这也是当您将错误类型的 ID 作为第二个参数传递给 em.find() 之类的东西(即传递实体本身而不是其 ID)时引发的错误。 我还没有发现实际声明 JPA 实体可序列化的必要性——除非您使用 aman 描述的 referencedColumnName,否则这不是真的必要。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM