为什么 Hibernate 不需要参数构造函数?

无参数构造函数是 需求(像 Hibernate 这样的工具使用 对这个构造函数的反射 实例化对象)。

我得到了这个答案,但有人可以进一步解释吗? 谢谢

108354 次浏览

Hibernate 实例化您的对象。所以它需要能够实例化它们。如果没有 no-arg 构造函数,Hibernate 就不知道 怎么做要实例化它,也就是说要传递什么参数。

休眠文档表示:

实现无参数构造函数

所有持久化类都必须有一个缺省构造函数(可以是非公共的) ,这样 Hibernate 才能使用 Constructor.newInstance()实例化它们。建议您在 Hibernate 中使用一个至少具有包可见性的缺省构造函数来生成运行时代理。

Hibernate 需要根据您的查询(通过反射)创建实例,Hibernate 依赖于实体的无参数构造函数,因此您需要提供无参数构造函数。什么不清楚?

Hibernate 和通常通过反射创建对象的代码使用 Class<T>.newInstance()创建类的新实例。此方法需要一个公共无参构造函数来实例化对象。对于大多数用例,提供无参构造函数不是问题。

有一些基于序列化的技巧可以解决没有无参数构造函数的问题,因为序列化使用 jvm magic 在不调用构造函数的情况下创建对象。但这并不适用于所有虚拟机。例如,XStream可以创建没有公共非参数构造函数的对象实例,但只能通过在所谓的“增强”模式下运行,这种模式只能在某些 VM 上使用。(详情请参阅连结。)Hibernate 的设计人员肯定选择保持与所有 VM 的兼容性,因此避免了这种技巧,并使用官方支持的反射方法 Class<T>.newInstance(),该方法需要一个无参数构造函数。

实际上,您可以实例化没有0-args 构造函数的类; 您可以获得类的构造函数列表,选择一个并使用伪参数调用它。

虽然这是可能的,我想这将工作,不会成为问题,你必须同意这是非常奇怪的。

像 Hibernate 那样构造对象(我相信它调用0-arg 构造函数,然后它可能通过反射直接修改实例的字段。也许它知道如何调用 setter)与 Java 中应该如何构造对象稍有不同——使用适当的参数调用构造函数,这样新对象就是您想要的对象。我相信,实例化一个对象,然后对其进行变异有点“反 Java”(或者我会说,反纯理论 Java)——当然,如果你通过直接字段操作这样做,它会进行封装和所有那些花哨的封装东西。

I think that the proper way to do this would be to define in the Hibernate mapping how an object should be instantiated from the info in the database row using the proper constructor... but this would be more complex- meaning both Hibernate would be even more complex, the mapping would be more complex... and all to be more "pure"; and I don't think this would have an advantage over the current approach (other than feeling good about doing things "the proper way").

话虽如此,而且考虑到 Hibernate 方法并不是非常“干净”,因此有一个0-arg 构造函数的义务并不是严格必要的,但是我可以理解这个需求,尽管我相信他们是基于纯粹的“正确方式”理由,当他们偏离了“正确方式”(尽管是出于合理的原因)很久以前。

Hibernate 是一个 ORM 框架,它支持字段或属性访问策略。但是,它不支持基于构造函数的映射-也许你会喜欢?因为有些问题

如果你的类包含大量的构造函数会发生什么

public class Person {


private String name;
private Integer age;


public Person(String name, Integer age) { ... }
public Person(String name) { ... }
public Person(Integer age) { ... }


}

正如您所看到的,您处理了一个不一致的问题,因为 Hibernate 无法假定应该调用哪个构造函数。例如,假设您需要检索存储的 Person 对象

Person person = (Person) session.get(Person.class, <IDENTIFIER>);

Hibernate 应该调用哪个构造函数来检索 Person 对象?

最后,通过使用反射,Hibernate 可以通过它的 no-arg 构造函数实例化一个类

Person person = (Person) session.get(Person.class, <IDENTIFIER>);

Hibernate 将实例化 Person 对象,如下所示

Person.class.newInstance();

根据 API 文档

The class is instantiated as if by a new expression with an 空荡荡的 argument list

Moral of the story

Person.class.newInstance();

类似于

new Person();

Nothing else

用无参数构造函数通过反射创建对象,然后用数据通过反射填充对象的属性,这比试图将数据匹配到参数化构造函数的任意参数要容易得多,比如改变名称/命名冲突、构造函数内部未定义的逻辑、参数集不匹配对象的属性等等。

Many ORMs and serializers require parameterless constructors, because paramterized constructors through reflection are very fragile, and parameterless constructors provide both stability to the application and control over the object behavior to the developer.

Hibernate uses proxies for lazy loading. If you do no define a constructor or make it private a few things may still work - the ones that do not depend on proxy mechanism. For example, loading the object (with no constructor) directly using query API.

但是,如果使用 session.load method () ,您将面临来自代理生成器 lib 的 InstantiationException,因为构造函数不可用。

This guy reported a similar situation:

Http://kristian-domagala.blogspot.com/2008/10/proxy-instantiation-problem-from.html

Check out this section of the Java language spec that explains the difference between static and non-static inner classes: http://java.sun.com/docs/books/jls/third_edition/html/classes.html#8.1.3

静态内部类在概念上与在.java 文件中声明的普通类没有什么不同。

由于 Hibernate 需要独立于 Project 实例来实例化 ProjectPK,因此 ProjectPK 要么需要是一个静态内部类,要么需要在其自身中声明。Java 文件。

reference 例外: 没有缺省构造函数

呃,对不起,各位,但是 Hibernate 的 不是要求您的类必须有一个无参数的构造函数。JPA 2.0规范需要它,这对 JPA 来说是非常蹩脚的。像 JAXB 这样的其他框架也需要它,这对于这些框架来说也是非常蹩脚的。

(实际上,JAXB 理应允许实体工厂,但它坚持自己实例化这些工厂,要求它们有一个——猜猜怎么着—— parameterless constructor,在我的书中,它完全等同于不允许工厂; 这是多么蹩脚!)

But Hibernate does not require such a thing.

Hibernate 支持一种拦截机制(参见 “拦截器”在文档中) ,它允许您使用所需的任何构造函数参数来实例化对象。

基本上,你所做的就是当你设置 hibernate 的时候,你传递给它一个实现 org.hibernate.Interceptor接口的对象,然后 hibernate 会在它需要你的对象的一个新实例的时候调用该接口的 instantiate()方法,所以你的方法的实现可以用任何你喜欢的方式 new你的对象。

我已经在一个项目中做到了这一点,它的工作就像一个魅力。在这个项目中,只要有可能,我就通过 JPA 做事情,只有在没有其他选择的时候,我才使用 Hibernate 特性,比如拦截器。

Hibernate 似乎对此有些不安全,因为在启动过程中,它会为我的每个实体类发出一条信息消息,告诉我 INFO: HHH000182: No default (no-argument) constructor for classclass must be instantiated by Interceptor,但后来我确实用拦截器实例化了它们,它对此很满意。

为了回答工具 除了冬眠问题中的“为什么”部分,答案是“绝对没有好的理由”,这已经被 hibernate 拦截器的存在所证明。有许多工具可以支持一些类似的客户端对象实例化机制,但是它们没有,所以它们自己创建对象,所以它们必须要求无参数构造函数。我倾向于相信这是因为这些工具的创造者认为他们自己是忍者系统程序员,他们创造了充满魔力的框架供无知的应用程序员使用,他们(所以他们认为)在他们最疯狂的梦想中永远不会需要像... 工厂模式这样的高级结构。(好吧,我是 诱惑的想法。我不这么认为。我开玩笑的。)

在我的例子中,我必须隐藏 no-arg 构造函数,但是因为 Hibernate,我不能这样做。所以我用另一种方法解决了这个问题。

/**
* @deprecated (Hibernate's exclusive constructor)
*/
public ObjectConstructor (){ }

下面总结一下。如果你想要兼容 JPA 或者严格的 Hibernate,这很重要

看看官方文件: https://docs.jboss.org/hibernate/orm/5.6/userguide/html_single/Hibernate_User_Guide.html#entity-pojo

Section 2.1 The Entity Class of the JPA 2.1 specification defines its requirements for an entity class. Applications that wish to remain portable across JPA providers should adhere to these requirements:

有一点是这样的:

实体类必须具有公共或受保护的无参数 它也可以定义其他的构造函数。

然而,Hibernate 在这方面没有那么严格:

然而,Hibernate 的要求并不严格:

One point says:

实体类必须具有无参数构造函数,该构造函数可以是 公共、受保护或包可见性。它可以定义额外的 还有构造函数。

More on that is right below: https://docs.jboss.org/hibernate/orm/5.6/userguide/html_single/Hibernate_User_Guide.html#entity-pojo-constructor

JPA 要求将此构造函数定义为 public 或 protected。在大多数情况下,Hibernate 并不关心构造函数的可见性,只要系统 SecurityManager 允许覆盖可见性设置。也就是说,如果您希望利用运行时代理生成,那么应该定义至少具有包可见性的构造函数。