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

时间:2010-05-29 16:43:59

标签: java hibernate orm factory-pattern

  

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

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

10 个答案:

答案 0 :(得分:121)

Hibernate,以及通过反射创建对象的一般代码使用Class<T>.newInstance()来创建类的新实例。此方法要求public no-arg构造函数能够实例化该对象。对于大多数用例,提供无参数构造函数不是问题。

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

答案 1 :(得分:44)

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

hibernate documentation说:

  

4.1.1。实现无参数构造函数

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

答案 2 :(得分:35)

嗯,对不起大家,但是Hibernate确实 要求您的类必须具有无参数构造函数。 JPA 2.0 specification需要它,而且代表JPA这是非常蹩脚的。像JAXB这样的其他框架也需要它,代表这些框架也是非常蹩脚的。

(实际上,JAXB据说允许实体工厂,但它坚持要自己实例化这些工厂,要求他们有一个--ghis什么 - 无参数构造函数,这在我的书中完全是好的,不允许工厂;这是多么蹩脚!)

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

Hibernate支持拦截机制(参见"Interceptor" in the documentation,),它允许您使用他们需要的任何构造函数参数来实例化对象。

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

我在一个项目中完成它,它就像一个魅力。在这个项目中,我尽可能通过JPA做事情,当我没有其他选择时,我只使用拦截器之类的Hibernate功能。

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

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

答案 3 :(得分:32)

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可以通过其无参数构造函数实例化一个类。所以当你打电话时

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

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

Person.class.newInstance();

根据API文档

  

通过 new 表达式将类实例化为参数列表

故事的道德

Person.class.newInstance();

类似于

new Person();

没有别的

答案 4 :(得分:6)

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

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

以Hibernate的方式构造对象(我相信它调用0-arg构造函数然后它可能直接通过Reflection修改实例的字段。也许它知道如何调用setter)稍微反对一个对象应该如何用Java构造 - 使用适当的参数调用构造函数,以便新对象是您想要的对象。我相信实例化一个对象然后改变它有点“反Java”(或者我会说,反纯理论Java) - 当然,如果你通过直接字段操作来实现这一点,它会封装和所有那些花哨的封装东西

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

说到这一点,并且看到Hibernate方法不是很“干净”,有一个0-arg构造函数的义务并不是绝对必要的,但我可以理解一下这个要求,尽管我相信他们纯粹是这样做的。当他们在此之前偏离“正确的方式”(尽管有合理的理由)时,“正确的方式”理由。

答案 5 :(得分:5)

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

答案 6 :(得分:2)

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

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

答案 7 :(得分:1)

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

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

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

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

答案 8 :(得分:0)

查看Java语言规范的这一部分,解释静态和非静态内部类之间的区别:http://java.sun.com/docs/books/jls/third_edition/html/classes.html#8.1.3

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

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

参考org.hibernate.InstantiationException: No default constructor

答案 9 :(得分:0)

通常在调用INFO Superclass data can be changed from the subclass, new value appears everywhere: INFO main: Creating a new LambdaConfig, which creates a new BaseConfig INFO BaseConfig.__init__: set self.base_data = '{'aws_lambda': {'dir': 'old_dir', 'name': 'old_name', 'id': 'old_id'}, 'more_keys': {'key1': 'old_value1', 'key2': 'old_value2'}}' INFO BaseConfig: self.data getter returning = '{'aws_lambda': {'dir': 'old_dir', 'name': 'old_name', 'id': 'old_id'}, 'more_keys': {'key1': 'old_value1', 'key2': 'old_value2'}}' INFO BaseConfig: self.data getter returning = '{'aws_lambda': {'dir': 'old_dir', 'name': 'old_name', 'id': 'old_id'}, 'more_keys': {'key1': 'old_value1', 'key2': 'old_value2'}}' INFO BaseConfig: self.data getter returning = '{'aws_lambda': {'dir': 'old_dir', 'name': 'old_name', 'id': 'old_id'}, 'more_keys': {'key1': 'old_value1', 'key2': 'old_value2'}}' INFO BaseConfig: self.data setter, new value for self.base_data='{'aws_lambda': {'dir': 'old_dir', 'name': 'old_name', 'id': 'old_id'}, 'more_keys': {'key1': 'old_value1', 'key2': 'old_value2'}}' INFO BaseConfig: Pretending to save self.base_data='{'aws_lambda': {'dir': 'old_dir', 'name': 'old_name', 'id': 'old_id'}, 'more_keys': {'key1': 'old_value1', 'key2': 'old_value2'}}' INFO BaseConfig: self.data getter returning = '{'aws_lambda': {'dir': 'old_dir', 'name': 'old_name', 'id': 'old_id'}, 'more_keys': {'key1': 'old_value1', 'key2': 'old_value2'}}' INFO main: aws_lambda_data = {'dir': 'old_dir', 'name': 'old_name', 'id': 'old_id'} INFO INFO BaseConfig: self.data getter returning = '{'aws_lambda': {'dir': 'old_dir', 'name': 'old_name', 'id': 'old_id'}, 'more_keys': {'key1': 'old_value1', 'key2': 'old_value2'}}' INFO LambdaConfig: dir setter before setting to new_dir is 'old_dir' INFO BaseConfig: self.data getter returning = '{'aws_lambda': {'dir': 'old_dir', 'name': 'old_name', 'id': 'old_id'}, 'more_keys': {'key1': 'old_value1', 'key2': 'old_value2'}}' INFO BaseConfig: self.data getter returning = '{'aws_lambda': {'dir': 'new_dir', 'name': 'old_name', 'id': 'old_id'}, 'more_keys': {'key1': 'old_value1', 'key2': 'old_value2'}}' INFO BaseConfig: self.data setter, new value for self.base_data='{'aws_lambda': {'dir': 'new_dir', 'name': 'old_name', 'id': 'old_id'}, 'more_keys': {'key1': 'old_value1', 'key2': 'old_value2'}}' INFO BaseConfig: Pretending to save self.base_data='{'aws_lambda': {'dir': 'new_dir', 'name': 'old_name', 'id': 'old_id'}, 'more_keys': {'key1': 'old_value1', 'key2': 'old_value2'}}' INFO BaseConfig: self.data getter returning = '{'aws_lambda': {'dir': 'new_dir', 'name': 'old_name', 'id': 'old_id'}, 'more_keys': {'key1': 'old_value1', 'key2': 'old_value2'}}' INFO LambdaConfig.dir setter after set: self.lambda_data['dir'] = 'new_dir' INFO main: after setting lambda_config.dir='new_dir', aws_lambda_data['dir'] = new_dir INFO main: aws_lambda_data = {'dir': 'new_dir', 'name': 'old_name', 'id': 'old_id'} INFO main: aws_lambda_data['dir'] = 'new_dir' INFO INFO BaseConfig: self.data getter returning = '{'aws_lambda': {'dir': 'new_dir', 'name': 'old_name', 'id': 'old_id'}, 'more_keys': {'key1': 'old_value1', 'key2': 'old_value2'}}' INFO BaseConfig: self.data getter returning = '{'aws_lambda': {'dir': 'new_dir', 'name': 'new_name', 'id': 'old_id'}, 'more_keys': {'key1': 'old_value1', 'key2': 'old_value2'}}' INFO BaseConfig: self.data setter, new value for self.base_data='{'aws_lambda': {'dir': 'new_dir', 'name': 'new_name', 'id': 'old_id'}, 'more_keys': {'key1': 'old_value1', 'key2': 'old_value2'}}' INFO BaseConfig: Pretending to save self.base_data='{'aws_lambda': {'dir': 'new_dir', 'name': 'new_name', 'id': 'old_id'}, 'more_keys': {'key1': 'old_value1', 'key2': 'old_value2'}}' INFO main: after setting lambda_config.name='new_name', aws_lambda_data['name'] = new_name INFO main: aws_lambda_data = {'dir': 'new_dir', 'name': 'new_name', 'id': 'old_id'} INFO main: aws_lambda_data['name'] = 'new_name' INFO BaseConfig: self.data getter returning = '{'aws_lambda': {'dir': 'new_dir', 'name': 'new_name', 'id': 'old_id'}, 'more_keys': {'key1': 'old_value1', 'key2': 'old_value2'}}' INFO BaseConfig: self.data getter returning = '{'aws_lambda': {'dir': 'new_dir', 'name': 'new_name', 'id': 'new_id'}, 'more_keys': {'key1': 'old_value1', 'key2': 'old_value2'}}' INFO BaseConfig: self.data setter, new value for self.base_data='{'aws_lambda': {'dir': 'new_dir', 'name': 'new_name', 'id': 'new_id'}, 'more_keys': {'key1': 'old_value1', 'key2': 'old_value2'}}' INFO BaseConfig: Pretending to save self.base_data='{'aws_lambda': {'dir': 'new_dir', 'name': 'new_name', 'id': 'new_id'}, 'more_keys': {'key1': 'old_value1', 'key2': 'old_value2'}}' INFO main: after setting lambda_config.id='new_id', aws_lambda_data['id'] = new_id INFO main: aws_lambda_data = {'dir': 'new_dir', 'name': 'new_name', 'id': 'new_id'} INFO main: aws_lambda_data['id'] = 'new_id' get()方法时,Hibernate使用Reflection API创建Entity Bean的实例。方法load()用于此目的,它需要Class.newInstance()构造函数。因此,如果您在实体Bean中没有no-args构造函数,则hibernate将无法实例化它,并且您将获得HibernateException。