Why does Hibernate require no argument constructor?
Asked Answered
F

11

127

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

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

Fictionist answered 29/5, 2010 at 16:43 Comment(5)
FYI: the assertion that The no-argument constructor is a requirement is wrong, and all answers that proceed to explain why this is so it without questioning whether this is in fact so, (including the accepted answer, which has even received bounty,) are wrong. See this answer: https://mcmap.net/q/174223/-why-does-hibernate-require-no-argument-constructorMalignity
It IS required if you're using hibernate as a provider for JPA.Mariannemariano
@MikeNakis You are incorrect Mike. Hibernate requires a default constructor to instantiate objects if you're using hibernate as a provider for JPA (Amalgovinus) otherwise Hibernate will report Caused by: org.hibernate.InstantiationException: No default constructor for entity: : hibernate.tutorial.Student as in the case in which I just encounteredEloyelreath
@Eloyelreath the question is tagged with "hibernate" and "orm", it is not tagged with "jpa". There is no mention of JPA in the question.Malignity
@MikeNakis I agree Mike but Hibernate is used as an implementation of "JPA" and not used in the absence of "JPA" or "ORM". Therefore, the assumption is hibernate is implementing "JPA".Eloyelreath
S
159

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.

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, XStream can 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.

Seafowl answered 4/6, 2010 at 5:32 Comment(9)
FYI: The constructor does not need to be public. It can have package visibility and Hibernate should setAccessible(true) on it.Eberhardt
Am i able to create a Custom UserType with a non default constructor in order to set a needed field for its operations.Merrygoround
For reference ObjectInputStream does something along the lines of sun.reflect.ReflectionFactory.getReflectionFactory().newConstructorForSerialization(classToGetInstanceOf, Object.class.getConstructor()).newInstance() for instantiating objects with no default constructor (JDK1.6 for Windows)Starchy
re: It can have package visibility and Hibernate should setAccessible(true). Does it mean the class being instantiated via reflection? And what does Hibernate should setAccessible(true) mean?Retirement
Objenesis does this and is widely used by many framework like spring-data and mockito github.com/easymock/objenesisCinquecento
@KevinMeredith This refers to Java Reflection API. Once you hold a reference to a field, you can basically force the field to be accessible as if it was public. I think, it's a good thing you don't know about the Reflection API, you will live longer and happier.Elfredaelfrida
Why not use Unsafe to instantiate without even calling constructor?Chinaman
"create a new instance of your classes" And then it uses setter for setting fields value?Subside
Hi @mdma, i am using spring-data-jpa-2.2.4.RELEASE, having hibernate-core-5.4.10.Final as the default implementor. The thing is I don't have a no-args constructor in all of my entities, and yet i don't see any issue. Any idea?Asshur
M
69

Erm, sorry everyone, but Hibernate does not require that your classes must have a parameterless constructor. The JPA 2.0 specification requires 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.

(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!)

But Hibernate does not require such a thing.

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

Basically, what you do is that when you setup hibernate you pass it an object implementing the org.hibernate.Interceptor interface, 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 new your objects in whatever way you like.

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.

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 class and class must be instantiated by Interceptor, but then later on I do instantiate them by interceptor, and it is happy with that.

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 tempted to think so. I don't actually think so. I am joking.)

Malignity answered 3/4, 2015 at 13:34 Comment(3)
Finally, someone who gets it! I've spent more of my time than I like dealing with these frameworks obscuring the object instantiation process (which is absolutely crucial for proper dependency injection and for rich object behaviour). Furthermore, Java reflection allows you to create objects without using newInstance(). The method getDeclaredConstructors has been in the reflection API since JDK 1.1. It's terrifying that the JPA spec designers neglected this.Bordelaise
This is wrong. If Hibernate is used as the JPA provider for persistence, it does require a default constructor otherwise the following ensues Caused by: org.hibernate.InstantiationException: No default constructor for entity: : hibernate.tutorial.Student which recently occurred because javax.persistence.*; is utilized and only org.hibernate when creating the Session, SessionFactory, and ConfigurationEloyelreath
@Eloyelreath This is perfectly correct, because a) the question is about hibernate, without a single mention of JPA, and b) I do nonetheless explicitly mention in the second sentence of my answer that JPA does require default constructors even though hibernate does not.Malignity
A
52

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 how to instantiate it, i.e. what argument to pass.

The hibernate documentation says:

4.1.1. Implement a no-argument constructor

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.

Antwanantwerp answered 29/5, 2010 at 16:47 Comment(6)
As for the constructor visibility, if you are using JPA v2.0 notice that the JSR-317 says: The no-arg constructor must be public or protected.Voltammeter
@Antwanantwerp hello sir, I have one doubt that if internally hibernate use Constructor.newInstance() to instantiate object then how hibernate set values into fields without any setters defined ?Southward
I don't get why I'm seeing this warning for an @Embeddable non-private subclass with a public no-arg constructor...Mariannemariano
Constructor.newInstance() takes arguments, the issue (actually a non-issue) is mapping those arguments. No idea why hibernate didn't solve this problem. For comparison: the @JsonCreator annotation in Jackson does this, and much benefit of immutable objects was had.Bordelaise
@ could you please look at my problem How to write HQL JOIN query for multiple table's selected Columns using Constructor In The Select ClauseBelemnite
Hi @Bozho, i am using spring-data-jpa-2.2.4.RELEASE, having hibernate-core-5.4.10.Final as the default implementor. The thing is I don't have a no-args constructor in all of my entities, and yet i don't see any issue. Any idea?Asshur
E
40

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

What happens whether your class contains a lot of constructors

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

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

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

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

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

Hibernate will instantiate your Person object as follows

Person.class.newInstance();

Which according to API documentation

The class is instantiated as if by a new expression with an empty argument list

Moral of the story

Person.class.newInstance();

is similar To

new Person();

Nothing else

Eductive answered 9/6, 2010 at 3:25 Comment(4)
This is by far the most excellent description I have found in regard to this question. Most of the answers I had found used bookish technical terms and no body explained it a pliable way like you did. Kudos to you and thanks !Eduardo
This might well be the Hibernate team's reasoning. But in actuality, the issues could be solved by (1) requiring either an annotation, or else only using a non-default constructor if there is only one constructor and (2) using class.getDeclaredConstructors. And using Constructor.newInstance() instead of Class.newInstance(). Appropriate mapping in XML/annotations would be needed, before Java 8, but it is entirely doable.Bordelaise
Ok, so hibernate creates object from default constructor, and then it uses setters for fields name and age? If not then it uses another constructor later?Subside
@Subside Yes, once instantiated, Hibernate uses the setters or fields - It depends on the access strategy. By default, the placement of the Id annotation gives the default access strategy. See docs.jboss.org/hibernate/orm/5.1/userguide/html_single/chapters/…Eductive
T
6

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?

Trundle answered 29/5, 2010 at 16:50 Comment(2)
Under what conditions is a private constructor incorrect? I'm seeing java.lang.InstantiationException even with a private constructor for my JPA entity. reference.Retirement
I tried class without empty constructor (but with args constructor) and it worked. I got INFO from hibernate "INFO: HHH000182: No default (no-argument) constructor for class and class must be instantiated by Interceptor", but there was no exception and object was successfully received from DB.Reiter
R
6

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.

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.

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").

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.

Riker answered 8/6, 2010 at 21:46 Comment(0)
G
2

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.

Gluey answered 10/6, 2010 at 8:48 Comment(1)
I'd argue that forcing complete mutability on what may need to be a rich domain object is more fragile still (it's not much of an ORM if your entities need to be featureless bags of data to work -- I'm here because I want a constructor but instead have an undefined order of rich setter calls) ... But +1 because you acknowledge that reflection can be performed on constructors with args :)Bordelaise
Q
2

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.

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

This guy reported a similar situation:

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

Quadruplet answered 28/10, 2011 at 11:23 Comment(0)
E
1

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

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

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.

reference org.hibernate.InstantiationException: No default constructor

Escalera answered 13/10, 2014 at 2:29 Comment(0)
D
1

In my case, I had to hide my no-arg constructor, but because Hibernate I couldn't do it. So I solved the problem in another way.

/**
 * @deprecated (Hibernate's exclusive constructor)
 */
public ObjectConstructor (){ }
Durnan answered 18/8, 2021 at 14:53 Comment(0)
G
0

Summarizing of what is below. It matters if you want to be JPA compatible or strictly Hibernate

Just look at official documentation: 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:

One point says:

The entity class must have a public or protected no-argument constructor. It may define additional constructors as well.

However, hibernate is less strict in this:

Hibernate, however, is not as strict in its requirements. The differences from the list above include:

One point says:

The entity class must have a no-argument constructor, which may be public, protected or package visibility. It may define additional constructors as well.

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 requires that this constructor be defined as public or protected. Hibernate, for the most part, does not care about the constructor visibility, as long as the system SecurityManager allows overriding the visibility setting. That said, the constructor should be defined with at least package visibility if you wish to leverage runtime proxy generation.

Gillie answered 27/2, 2022 at 8:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.