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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2935826/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 14:37:56  来源:igfitidea点击:

Why does Hibernate require no argument constructor?

javahibernateormfactory-pattern

提问by unj2

The no-argument constructor is a requirement (tools like Hibernate use reflection on this constructor to instantiate objects).

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

I got this hand-wavy answer but could somebody explain further? Thanks

我得到了这个手工波浪式的答案,但有人可以进一步解释吗?谢谢

采纳答案by mdma

Hibernate, and code in general that creates objects via reflection use Class<T>.newInstance()to create a new instance of your classes. This method requires a public no-arg constructor to be able to instantiate the object. For most use cases, providing a no-arg constructor is not a problem.

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

There are hacks based on serialization that can work around not having a no-arg constructor, since serialization uses jvm magic to create objects without invoking the constructor. But this is not available across all VMs. For example, XStreamcan create instances of objects that don't have a public no-arg constructor, but only by running in a so-called "enhanced" mode which is available only on certain VMs. (See the link for details.) Hibernate's designers surely chose to maintain compatibility with all VMs and so avoids such tricks, and uses the officially supported reflection method Class<T>.newInstance()requiring a no-arg constructor.

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

回答by Bozho

Hibernate instantiates your objects. So it needs to be able to instantiate them. If there isn't a no-arg constructor, Hibernate won't know howto instantiate it, i.e. what argument to pass.

Hibernate 实例化你的对象。所以它需要能够实例化它们。如果没有无参数构造函数,Hibernate 将不知道如何实例化它,即传递什么参数。

The hibernate documentationsays:

Hibernate文档说:

4.1.1. Implement a no-argument constructor

4.1.1. 实现无参数构造函数

All persistent classes must have a default constructor (which can be non-public) so that Hibernate can instantiate them using Constructor.newInstance(). It is recommended that you have a default constructor with at least package visibility for runtime proxy generation in Hibernate.

所有持久类都必须有一个默认构造函数(可以是非公共的),以便 Hibernate 可以使用Constructor.newInstance(). 建议您在 Hibernate 中使用至少具有包可见性的默认构造函数来生成运行时代理。

回答by Pascal Thivent

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?

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

回答by alex

Actually, you can instantiate classes which have no 0-args constructor; you can get a list of a class' constructors, pick one and invoke it with bogus parameters.

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

While this is possible, and I guess it would work and wouldn't be problematic, you'll have to agree that is pretty weird.

虽然这是可能的,而且我想它会起作用并且不会有问题,但您必须同意这很奇怪。

Constructing objects the way Hibernate does (I believe it invokes the 0-arg constructor and then it probably modifies the instance's fields directly via Reflection. Perhaps it knows how to call setters) goes a little bit against how is an object supposed to be constructed in Java- invoke the constructor with the appropriate parameters so that the new object is the object you want. I believe that instantiating an object and then mutating it is somewhat "anti-Java" (or I would say, anti pure theoretical Java)- and definitely, if you do this via direct field manipulation, it goes encapsulation and all that fancy encapsulation stuff.

以 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 映射中定义如何使用适当的构造函数从数据库行中的信息实例化对象......但这会更复杂 - 这意味着 Hibernate 将是偶数更复杂,映射会更复杂……而且都更“纯”;而且我认为这不会比当前的方法有优势(除了对“以正确的方式”做事感觉良好之外)。

Having said that, and seeing that the Hibernate approach is not very "clean", the obligation to have a 0-arg constructor is not strictly necessary, but I can understand somewhat the requirement, although I believe they did it on purely "proper way" grounds, when they strayed from the "proper way" (albeit for reasonable reasons) much before that.

话虽如此,并且看到 Hibernate 方法不是很“干净”,拥有 0-arg 构造函数的义务并不是绝对必要的,但我可以理解一些要求,尽管我相信他们是完全以“正确的方式”做到的”的理由,当他们在此之前就偏离了“正确的方式”(尽管出于合理的原因)。

回答by Arthur Ronald

The hibernate is an ORM framework which supports field or property access strategy. However, it does not support constructor-based mapping - maybe what you would like ? - because of some issues like

Hibernate 是一个支持字段或属性访问策略的 ORM 框架。但是,它不支持基于构造函数的映射——也许你想要什么?- 因为一些问题,比如

1oWhat happens whether your class contains a lot of constructors

1o如果你的类包含很多构造函数会发生什么

public class Person {

    private String name;
    private Integer age;

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

}

As you can see, you deal with a issue of inconsistency because Hibernate cannot suppose which constructor should be called. For instance, suppose you need to retrieve a stored Person object

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

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

Which constructor should Hibernate call to retrieve a Person object ? Can you see ?

Hibernate 应该调用哪个构造函数来检索 Person 对象?你能看到吗你能明白吗 ?

2oAnd finally, by using reflection, Hibernate can instantiate a class through its no-arg constructor. So when you call

2o最后,通过使用反射,Hibernate 可以通过其无参数构造函数实例化一个类。所以当你打电话

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

Hibernate will instantiate your Person object as follows

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

Person.class.newInstance();

Which according to API documentation

其中根据API文档

The class is instantiated as if by a newexpression with an emptyargument list

类被实例化,就好像由一个带有参数列表的表达式

Moral of the story

故事的道德启示

Person.class.newInstance();

is similar To

类似于

new Person();

Nothing else

没有其他的

回答by Kaerber

It is much easier to create object with a parameterless constructor through reflection, and then fill its properties with data through reflection, than to try and match data to arbitrary parameters of a parameterized constructor, with changing names/naming conflicts, undefined logic inside constructor, parameter sets not matching properties of an object, et cetera.

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

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.

许多 ORM 和序列化器都需要无参数构造函数,因为通过反射的参数化构造函数非常脆弱,而无参数构造函数既为应用程序提供稳定性,又为开发人员提供对对象行为的控制。

回答by haps10

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.

Hibernate 使用代理进行延迟加载。如果您没有定义构造函数或将其设为私有,那么一些事情可能仍然有效——那些不依赖于代理机制的事情。例如,直接使用查询 API 加载对象(没有构造函数)。

But, if you use session.load method() you'll face InstantiationException from proxy generator lib due to non-availability of constructor.

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

This guy reported a similar situation:

这家伙报告了类似的情况:

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

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

回答by Amitābha

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 语言规范的这一部分:http: //java.sun.com/docs/books/jls/third_edition/html/classes.html#8.1.3

A static inner class is conceptually no different than a regular general class declared in a .java file.

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

Since Hibernate needs to instantiate ProjectPK independantly of the Project instance, ProjectPK either needs to be a static inner class, or declared in it's own .java file.

由于 Hibernate 需要独立于 Project 实例实例化 ProjectPK,因此 ProjectPK 要么需要是静态内部类,要么在它自己的 .java 文件中声明。

reference org.hibernate.InstantiationException: No default constructor

参考org.hibernate.InstantiationException: 没有默认构造函数

回答by Mike Nakis

Erm, sorry everyone, but Hibernate does notrequire that your classes must have a parameterless constructor. The JPA 2.0 specificationrequires it, and this is very lame on behalf of JPA. Other frameworks like JAXB also require it, which is also very lame on behalf of those frameworks.

呃,对不起大家,但是Hibernate并没有要求你的类必须有一个参数的构造函数。在JPA 2.0规范需要它,这是代表JPA的非常跛脚。其他框架如 JAXB 也需要它,这对于那些框架来说也是非常蹩脚的。

(Actually, JAXB supposedly allows entity factories, but it insists on instantiating these factories by itself, requiring them to have a --guess what-- parameterless constructor, which in my book is exactly as good as not allowing factories; how lame is that!)

(实际上,JAXB 应该允许实体工厂,但它坚持自己实例化这些工厂,要求它们有一个 --guess what-- parameterless constructor,在我的书中这与不允许工厂一样好;这有多蹩脚!)

But Hibernate does not require such a thing.

但是 Hibernate 不需要这样的东西。

Hibernate supports an interception mechanism, (see "Interceptor" in the documentation,) which allows you to instantiate your objects with whatever constructor parameters they need.

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

Basically, what you do is that when you setup hibernate you pass it an object implementing the org.hibernate.Interceptorinterface, and hibernate will then be invoking the instantiate()method of that interface whenever it needs a new instance of an object of yours, so your implementation of that method can newyour objects in whatever way you like.

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

I have done it in a project and it works like a charm. In this project I do things via JPA whenever possible, and I only use Hibernate features like the interceptor when I have no other option.

我在一个项目中完成了它,它就像一个魅力。在这个项目中,我尽可能通过 JPA 来做事情,并且我只在没有其他选择的情况下使用 Hibernate 特性,比如拦截器。

Hibernate seems to be somewhat insecure about it, as during startup it issues an info message for each of my entity classes, telling me INFO: HHH000182: No default (no-argument) constructor for classand class must be instantiated by Interceptor, but then later on I do instantiate them by interceptor, and it is happy with that.

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

To answer the "why" part of the question for tools other than Hibernate, the answer is "for absolutely no good reason", and this is proven by the existence of the hibernate interceptor. There are many tools out there that could have been supporting some similar mechanism for client object instantiation, but they don't, so they create the objects by themselves, so they have to require parameterless constructors. I am tempted to believe that this is happening because the creators of these tools think of themselves as ninja systems programmers who create frameworks full of magic to be used by ignorant application programmers, who (so they think) would never in their wildest dreams have a need for such advanced constructs as the... Factory Pattern. (Okay, I am temptedto think so. I don't actuallythink so. I am joking.)

要回答Hibernate 以外的工具的问题的“为什么”部分,答案是“绝对没有充分的理由”,这可以通过 hibernate 拦截器的存在来证明。有许多工具可以支持客户端对象实例化的一些类似机制,但它们没有,因此它们自己创建对象,因此它们必须需要无参数构造函数。我很想相信这正在发生,因为这些工具的创建者认为自己是忍者系统程序员,他们创建了充满魔力的框架供无知的应用程序程序员使用,他们(他们认为)永远不会在他们最疯狂的梦想中拥有需要诸如... Factory Pattern 之类的高级构造。(好的,这么认为。我其实不这么认为。开玩笑。)