无参数构造函数是必需的(像 Hibernate 这样的工具使用此构造函数上的反射来实例化对象)。
我得到了这个挥手的答案,但有人可以进一步解释吗?谢谢
无参数构造函数是必需的(像 Hibernate 这样的工具使用此构造函数上的反射来实例化对象)。
我得到了这个挥手的答案,但有人可以进一步解释吗?谢谢
Hibernate 和通过反射创建对象的一般代码用于Class<T>.newInstance()
创建类的新实例。此方法需要一个公共的无参数构造函数才能实例化对象。对于大多数用例,提供无参数构造函数不是问题。
有一些基于序列化的技巧可以解决没有无参数构造函数的问题,因为序列化使用 jvm 魔法来创建对象而不调用构造函数。但这并非适用于所有虚拟机。例如,XStream可以创建没有公共无参数构造函数的对象实例,但只能通过运行在仅在某些 VM 上可用的所谓“增强”模式来实现。(有关详细信息,请参阅链接。)Hibernate 的设计者肯定选择保持与所有 VM 的兼容性,因此避免了这些技巧,并使用了官方支持的反射方法Class<T>.newInstance()
,需要一个无参数的构造函数。
呃,对不起大家,但是 Hibernate 并不要求你的类必须有一个无参数的构造函数。JPA 2.0 规范需要它,而这对于JPA 来说是非常蹩脚的。像 JAXB 这样的其他框架也需要它,这对于那些框架来说也很蹩脚。
(实际上,JAXB 应该允许实体工厂,但它坚持自己实例化这些工厂,要求它们有一个 --guess what-- 无参数构造函数,在我的书中这与不允许工厂一样好;那是多么蹩脚!)
但是Hibernate不需要这样的东西。
Hibernate 支持一种拦截机制,(请参阅文档中的“拦截器”),它允许您使用所需的任何构造函数参数来实例化您的对象。
基本上,您所做的是,当您设置 hibernate 时,您将一个实现org.hibernate.Interceptor
接口的对象传递给它,然后当它需要您的对象的新实例时,hibernate 将调用该instantiate()
接口的方法,因此您对该方法的实现可以new
以任何你喜欢的方式你的对象。
我在一个项目中完成了它,它就像一个魅力。在这个项目中,我尽可能通过 JPA 做事,并且只有在没有其他选择时才使用拦截器等 Hibernate 功能。
Hibernate 似乎对此有些不安全,因为在启动期间它会为我的每个实体类发出一条信息消息,告诉我INFO: HHH000182: No default (no-argument) constructor for class
and class must be instantiated by Interceptor
,但后来我确实通过拦截器实例化它们,并且对此感到满意。
要回答除 Hibernate 以外的工具的问题的“为什么”部分,答案是“绝对没有充分的理由”,这可以通过 hibernate 拦截器的存在来证明。有许多工具可以支持一些类似的客户端对象实例化机制,但它们不支持,因此它们自己创建对象,因此它们必须需要无参数构造函数。我很想相信这种情况正在发生,因为这些工具的创建者认为自己是忍者系统程序员,他们创建了充满魔法的框架,供无知的应用程序程序员使用,他们(他们认为)在他们最疯狂的梦想中永远不会有一个需要诸如...工厂模式之类的高级构造。(好的,这么想。我其实不这么认为。开玩笑。)
Hibernate 实例化你的对象。所以它需要能够实例化它们。如果没有无参数构造函数,Hibernate 将不知道如何实例化它,即传递什么参数。
休眠文档说:
4.1.1。实现无参数构造函数
所有持久类都必须有一个默认构造函数(可以是非公共的),以便 Hibernate 可以使用Constructor.newInstance()
. 建议您有一个默认构造函数,至少具有包可见性,以便在 Hibernate 中生成运行时代理。
hibernate 是一个支持字段或属性访问策略的 ORM 框架。但是,它不支持基于构造函数的映射——也许你想要什么?- 因为一些问题,比如
1º如果你的类包含很多构造函数会发生什么
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 对象?你能看到吗你能明白吗 ?
2º最后,通过使用反射,Hibernate 可以通过其无参数构造函数实例化一个类。所以当你打电话
Person person = (Person) session.get(Person.class, <IDENTIFIER>);
Hibernate 将按如下方式实例化您的 Person 对象
Person.class.newInstance();
根据 API 文档
该类被实例化为一个带有空参数列表的新表达式
故事的道德启示
Person.class.newInstance();
类似于
new Person();
没有其他的
Hibernate needs to create instances as result of your queries (via reflection), Hibernate relies on the no-arg constructor of entities for that, so you need to provide a no-arg constructor. What is not clear?
实际上,您可以实例化没有 0-args 构造函数的类;您可以获取一个类的构造函数列表,选择一个并使用虚假参数调用它。
虽然这是可能的,而且我想它会起作用并且不会有问题,但您必须同意这很奇怪。
以 Hibernate 的方式构造对象(我相信它调用 0-arg 构造函数,然后它可能直接通过反射修改实例的字段。也许它知道如何调用 setter)有点违背应该如何构造对象Java - 使用适当的参数调用构造函数,以便新对象是您想要的对象。我相信实例化一个对象然后对其进行变异有点“反Java”(或者我会说,反纯理论Java)——当然,如果你通过直接字段操作来做到这一点,它就会进行封装和所有花哨的封装的东西.
我认为执行此操作的正确方法是在 Hibernate 映射中定义如何使用正确的构造函数从数据库行中的信息实例化对象......但这会更复杂 - 这意味着 Hibernate 将是偶数更复杂,映射会更复杂......而且都更“纯粹”;而且我认为这不会比当前的方法有优势(除了对“以正确的方式”做事感觉良好)。
话虽如此,并且看到 Hibernate 方法不是很“干净”,拥有 0-arg 构造函数的义务并不是绝对必要的,但我可以理解一些要求,尽管我相信他们是纯粹以“正确的方式”做到的” 理由,当他们在此之前偏离“正确的方式”(尽管出于合理的原因)。
Hibernate 使用代理进行延迟加载。如果您没有定义构造函数或将其设为私有,那么一些事情可能仍然有效 - 那些不依赖于代理机制的事情。例如,直接使用查询 API 加载对象(没有构造函数)。
但是,如果您使用 session.load 方法(),由于构造函数不可用,您将面临来自代理生成器库的 InstantiationException。
这家伙报告了类似的情况:
http://kristian-domagala.blogspot.com/2008/10/proxy-instantiation-problem-from.html
通过反射使用无参数构造函数创建对象,然后通过反射用数据填充其属性,比尝试将数据与参数化构造函数的任意参数匹配要容易得多,名称/命名冲突不断变化,构造函数内部未定义逻辑,参数集与对象的属性不匹配,等等。
许多 ORM 和序列化器都需要无参数构造函数,因为通过反射的参数化构造函数非常脆弱,而无参数构造函数既为应用程序提供稳定性,又为开发人员提供对对象行为的控制。
查看解释静态和非静态内部类之间区别的 Java 语言规范的这一部分:http: //java.sun.com/docs/books/jls/third_edition/html/classes.html#8.1.3
静态内部类在概念上与在 .java 文件中声明的常规通用类没有什么不同。
由于 Hibernate 需要独立于 Project 实例来实例化 ProjectPK,因此 ProjectPK 要么需要是静态内部类,要么在它自己的 .java 文件中声明。
总结以下内容。如果您想与 JPA 兼容或严格 Hibernate,这很重要
第 2.1 节 JPA 2.1 规范的实体类定义了它对实体类的要求。希望在 JPA 提供者之间保持可移植性的应用程序应遵守以下要求:
一点说:
实体类必须具有公共或受保护的无参数构造函数。它也可以定义额外的构造函数。
然而,hibernate 在这方面没有那么严格:
然而,Hibernate 的要求并没有那么严格。与上述列表的不同之处包括:
一点说:
实体类必须有一个无参数的构造函数,它可以是公共的、受保护的或包可见性。它也可以定义额外的构造函数。
JPA 要求将此构造函数定义为公共的或受保护的。Hibernate 在大多数情况下并不关心构造函数的可见性,只要系统 SecurityManager 允许覆盖可见性设置。也就是说,如果您希望利用运行时代理生成,则应至少使用包可见性定义构造函数。
在我的情况下,我不得不隐藏我的无参数构造函数,但是因为 Hibernate 我不能这样做。所以我用另一种方式解决了这个问题。
/**
* @deprecated (Hibernate's exclusive constructor)
*/
public ObjectConstructor (){ }