问题在标题中。下面我只是描述了我的一些想法和发现。
当我有一个非常简单的域模型(3 个没有任何关系的表)时,我的所有实体都没有实现 Serializable
接口。
但是当域模型变得更复杂时,我得到了一个 RuntimeException
,说我的一个实体没有实现 Serializable
。
我使用 Hibernate 作为 JPA 实现,我想知道:
它是供应商特定的要求/行为吗?我的可序列化实体会发生什么?它们应该可序列化以进行存储或传输吗?什么时候需要使我的实体可序列化?
根据 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 将您传入的类型映射到数据库可以理解的任何类型。当您运行本机 SQL 时,您必须自己进行映射。如果您不这样做,那么默认映射是将参数序列化并将其发送到数据库(希望它确实理解它)。
Serializable
是两个不同的概念。
JPA 规范
根据 JPA 规范,只有当实体需要从一个 JVM 传递到另一个 JVM 或实体由需要由 EJB 容器钝化的有状态会话 Bean 使用时,实体才应实现 Serializable
。
如果一个实体实例作为一个分离的对象按值传递(例如,通过一个远程接口),实体类必须实现 Serializable 接口。
休眠
Hibernate 只要求实体属性是 Serializable
,而不是实体本身。
但是,在实施 JPA 规范时,有关 Serializable
实体的所有 JPA 要求也适用于 Hibernate。
雄猫
根据 Tomcat documentation,HttpSession
属性也需要为 Serializable
:
每当 Apache Tomcat 正常关闭并重新启动时,或者当触发应用程序重新加载时,标准管理器实现将尝试将所有当前活动的会话序列化到通过路径名属性定位的磁盘文件。当应用程序重新加载完成时,所有这些保存的会话将被反序列化并激活(假设它们同时没有过期)。为了成功恢复会话属性的状态,所有此类属性必须实现 java.io.Serializable 接口。
因此,如果实体存储在 HttpSession
中,它应该实现 Serializable
。
根据hibernate docs,同时使用@JoinColumn 注解:
它还有一个名为referencedColumnName 的参数。此参数声明将用于连接的目标实体中的列。请注意,将 referencedColumnName 用于非主键列时,关联的类必须是可序列化的。
@JoinColumn
与 refenrecedColumnName
一起用于另一个表中的非主唯一列。这导致 ClassCastException
因为休眠无法序列化实体类。
如果我们只讨论持久性,则不需要 Serializable
但最好将实体设为 Serializable
。
如果我们将 domain
/entities
对象直接暴露给表示层,而不是使用 DTO
,那么我们需要实现 Serializable
。 这些域对象可以存储在 HTTPSession
中用于缓存/优化目的。 http-session 可以被序列化或集群化。在 JVM
实例之间传输数据也需要它。
当我们使用 DTO
来解耦持久层和服务层时,将域对象标记为 Serializable
会适得其反,并且会违反“encapsulation
”。然后它变成了一个反模式。
主键类必须是可序列化的。
如果要将实体实例作为分离对象远程使用,则实体类必须实现 Serializable
接口。
缓存
此外,如果您要实现 clustered
二级cache
,那么您的实体必须是 serializable
。标识符必须是 Serializable
,因为这是 JPA 要求,因为 identifier
可能用作二级缓存条目的键。
当我们序列化实体时,请确保提供显式 serialVersionUID
和私有访问修饰符。因为如果 serializable
类未显式声明 serialVersionUID
,则序列化运行时将根据类的各个方面为该类计算默认 serialVersionUID
值,如 Java(TM) Object Serialization Specification 中所述。默认 serialVersionUID
计算对可能因编译器实现而异的类细节高度敏感,因此可能会在反序列化期间导致意外的 InvalidClassExceptions
。
为了补充提到 JSR-317 规范的 Conor 的好答案。通常,EAR 项目包含一个 EJB 模块,其中 EJB 通过远程接口公开。在这种情况下,您需要使实体 bean 可序列化,因为它们在远程 EJB 中聚合并构建为通过网络连接。
没有 CDI 的 JEE6 战争项目:可以包含由不可序列化的 JPA 实体支持的 EJB lite。
带有 CDI 的 JEE6 战争项目:Beans that use session, application, or conversation scope must be serializable, but beans that use request scope do not have to be serializable. 因此,底层 JPA 实体 bean(如果有的话)将遵循相同的语义。
我相信您的问题与具有未注释的复杂类型(类)的字段有关。在这种情况下,默认处理将以序列化形式将对象存储在数据库中(这可能不是您想要做的)示例:
Class CustomerData {
int getAge();
void setAge(int age);
}
@Entity
Class Customer {
CustomerData getCustomerData();
void setCustomerData(CustomerData data)
}
在上述情况下,CustomerData 将以其序列化形式保存在数据库中的字节数组字段中。
如果要对类进行序列化,类必须实现 Serializable。这与 JPA 没有直接关系,并且 JPA 规范不要求实体是可序列化的。如果 Hibernate 真的抱怨这个,我想这是一个 Hibernate 错误,但我想你直接或间接地对实体做其他事情,这要求它们是可序列化的。
请参阅http://www.adam-bien.com/roller/abien/entry/do_jpa_entities_have_to,它说,通过 IIOP 或 JRMP (RMI) 在 JVM 实例之间传输数据只需要 java.io.Serializable 的实现。在纯 Web 应用程序的情况下,域对象有时存储在 HTTPSession 中以用于缓存/优化目的。 http-session 可以被序列化(钝化)或集群化。在这两种情况下,所有内容都必须是可序列化的。
什么时候需要使我的实体可序列化?
使用磁盘存储实现 ehcache 作为二级缓存(即在实体或存储库/服务方法上使用 @Cacheable
注释)需要可序列化,否则缓存将失败 (NotSerializableException
) 将实体写入磁盘缓存。
使用 postman 或 ajax 或 angular js 等进行远程点击,可能会导致 StackOverflow 异常与 Jackson fasterxml 的重复循环。因此,最好使用序列化程序。
当 JPA 实体被远程 EJB 操作用作参数或返回值时
这也是当您将输入错误的 ID 作为第二个参数传递给诸如 em.find() 之类的东西(即传递实体本身而不是其 ID)时引发的错误。我还没有发现实际声明 JPA 实体可序列化的必要性——除非您按照 aman 的描述使用 referencedColumnName,否则这并不是真正必要的。
不定期副业成功案例分享
Serializable
可能对此有所帮助,并且在持久性的上下文中比Cloneable
更一致。