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

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

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

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

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

  1. 它是特定于供应商的要求/行为吗?
  2. 我的可序列化实体会发生什么?对于存储或传输,它们应该是可序列化的吗?
  3. 在什么时候有必要使我的实体可序列化?
115173 次浏览

如果您需要通过网络传输它们(将它们序列化为其他一些表示),将它们存储在HTTP会话中(该会话又由servlet容器序列化到硬盘)等,则您需要将实体Serializable

只是出于持久性的考虑,Serializable是不需要的,至少对于Hibernate是这样。 但最佳做法是将它们Serializable

如果要序列化类,则必须实现Serializable.这与JPA没有直接关系,并且JPA规范不要求实体是可序列化的。如果Hibernate真的抱怨这一点,我认为这是一个Hibernate错误,但我认为您正在直接或间接地对实体执行其他操作,这要求它们是可序列化的。

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

根据JPA规范:

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

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

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

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


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

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

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

没有CDI的JEE6 WAR项目:可以包含由不可序列化的JPA实体支持的EJB Lite.

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

根据休眠文档,当使用@JoinColumn注释时:

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

这也是将类型不正确的ID作为第二个参数传递给em.find()(即传递实体本身而不是其ID)时抛出的错误。我还没有发现实际声明JPA实体可序列化的必要性——除非您使用Aman所描述的ReferencedColumnName,否则实际上没有必要。

使用Postman或Ajax或Angular JS等进行远程点击,可能会导致Jackson FasterXML出现StackOverflow异常的重复循环。因此,最好使用序列化程序。

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

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

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

如果我们只讨论持久性,则不需要Serializable,但最佳实践是使实体Serializable

如果我们要将_ABC_0/_ABC_1对象直接公开给表示层,而不是使用_ABC_2,在这种情况下,我们需要实现Serializable,这些域对象可以存储在HTTPSession中,用于高速缓存/优化目的。HTTP会话可以序列化或集群化。在JVM个实例之间传输数据时也需要它。

当我们使用DTO来分离持久层和服务层时,将域对象标记为Serializable将会适得其反,并且会违反“encapsulation ”。那么它就变成了反模式。

复合标识符

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

POJO模型

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

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

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

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

JPA规范

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

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

冬眠

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

但是,通过实现JPA规范,所有关于Serializable实体的JPA要求也适用于Hibernate.

雄猫

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

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

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

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