ChatGPT解决这个技术问题 Extra ChatGPT

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

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

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

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

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

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


A
Andrew Tobilko

根据 JPA 规范:

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

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


(+1)查看规范总是富有成效的
我不明白为什么这有这么多赞成票。 OP 说当模型更简单时不需要它。通过 Java 序列化远程发送对象总是要求对象是可序列化的,无论其复杂性如何。显然这不是 OP 的用例。
我不太确定 hibernate,但是对于其他 JPA 提供者,有些操作需要提供者制作实体(对象)的副本。 Serializable 可能对此有所帮助,并且在持久性的上下文中比 Cloneable 更一致。
这个答案只是一个信息转储,根本不能帮助人们理解原因。
B
Bozho

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

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


我不知道,也许我的实体被隐式转移到某个地方。我使用hibernate+spring+jsf和Tomcat。在这个链中的什么地方可以发生转移?
@Roman 例如,当前用户(可能是一个实体)及其所有相关实体可能最终都在会话中,正如 Bozho 所说,它可以由 servlet 容器序列化到磁盘。
Java 中的最佳实践是不要使类可序列化,除非出于遗留原因绝对必须这样做。请参阅 Josh Block 的有效 Java。 JPA 的最佳实践是什么?好的,这是一个旧答案,现在需要通过网络传输对象或存储在 http 会话中的情况应该很少见。
@dancarter - 这完全是胡说八道,也是对 Josh Bloch 关于该主题的建议的错误描述。序列化是语言和运行时的重要组成部分。它当然不是为支持“遗留”代码而保留的。在许多情况下,您应该或必须实现 Serializable。但是,您不应在没有充分理由且不了解后果的情况下这样做。 Josh 的观点是,该语言使得实现 Serializable 变得过于容易,而无需知道为什么或如何安全地这样做。
关于现在非常古老的原始帖子和这个同样古老的答案:JPA 提供程序在内存和辅助存储中广泛使用缓存。除了最简单的实现之外,这将需要对实体进行序列化。二级缓存通常是持久的,用于恢复和/或溢出到磁盘。 JPA 提供者有时还必须制作实体的“碳”副本,并且要求实现 Serializable 接口是支持这一点的最可靠方法。
A
Aaron Digulla

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


这并不能解释为什么只是“也许如何”见下面 Bozho 的回答
这意味着实体将保存在数据库中而不实现可序列化接口吗?
@Hanumantha_3048092 是的。实体映射和 Serializable 是两个不同的概念。
@AaronDigulla 您能否通过示例或伪代码进行解释。
K
Kevin Peters

JPA 规范

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

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

休眠

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

但是,在实施 JPA 规范时,有关 Serializable 实体的所有 JPA 要求也适用于 Hibernate。

雄猫

根据 Tomcat documentationHttpSession 属性也需要为 Serializable

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

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


N
Nathan Tuggy

根据hibernate docs,同时使用@JoinColumn 注解:

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


谢谢你的解释。我将 @JoinColumnrefenrecedColumnName 一起用于另一个表中的非主唯一列。这导致 ClassCastException 因为休眠无法序列化实体类。
A
Ankur Singhal

如果我们只讨论持久性,则不需要 Serializable 但最好将实体设为 Serializable

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

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

Composite identifiers

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

POJO Models

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

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

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


缓存+1。这个答案很详细。它只是降落晚了,所以分数不高。
@Ankur“如果我们将域/实体对象直接暴露给表示层,而不是使用 DTO ,在这种情况下我们需要实现 Serializable ”这是不正确的。根据 Joshua Bloch Effective Java “一旦对象被序列化,它的编码可以从一个 VM 发送到另一个 VM 或存储在磁盘上以供以后反序列化”。如果表示层是浏览器(在大多数情况下),对象序列化 java 内置序列化不会发生,因为浏览器不会理解反序列化流。
DTO 对象wo 序列化工作。是因为内部的所有实体都被序列化了吗?
S
Sym-Sym

为了补充提到 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(如果有的话)将遵循相同的语义。


N
Nathan Hughes

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

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

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

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


这个很有意思,只要CustomerData实现了Serializable,在加载Customer的时候就可以反序列化customerData?
j
jarnbjo

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


A
Arun K

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


M
Michal

什么时候需要使我的实体可序列化?

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


t
tamil

使用 postman 或 ajax 或 angular js 等进行远程点击,可能会导致 StackOverflow 异常与 Jackson fasterxml 的重复循环。因此,最好使用序列化程序。


S
SAR

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


A
Amalgovinus

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