Чому Hibernate не вимагає конструктора аргументів?


103

Конструктор no-argument є вимогою (такі інструменти, як Hibernate, використовують відображення цього конструктора для створення екземплярів об'єктів).

Я отримав цю хвилеподібну відповідь, але хтось може пояснити далі? Дякую


7
FYI: твердження, що The no-argument constructor is a requirement невірно , і всі відповіді, які продовжують пояснювати, чому це так, не ставлячи під сумнів, чи це насправді так (включаючи прийняту відповідь, яка навіть отримала щедрість), є помилковими . Дивіться цю відповідь: stackoverflow.com/a/29433238/773113
Mike Nakis

2
Це потрібно, якщо ви використовуєте сплячий режим як постачальник послуг JPA.
Амальговінус

1
@MikeNakis Ви неправильний Майк. Hibernate вимагає конструктора за замовчуванням для створення об'єктів, якщо ви використовуєте сплячий режим як постачальник для JPA (Amalgovinus), інакше Hibernate повідомить, Caused by: org.hibernate.InstantiationException: No default constructor for entity: : hibernate.tutorial.Studentяк у випадку, в якому я щойно зіткнувся
Mushy

@Mushy питання позначено тегами "сплячий" та "орм", воно не позначене "jpa". У питанні немає жодної згадки про JPA.
Mike Nakis

1
@MikeNakis Я погоджуюся з Майком, але Hibernate використовується як реалізація "JPA", а не використовується за відсутності "JPA" або "ORM". Тому припущення про сплячий режим реалізує "JPA".
Mushy

Відповіді:


138

Hibernate і код загалом, який створює об'єкти за допомогою відображення, використовуючи Class<T>.newInstance()для створення нового примірника ваших класів. Цей метод вимагає, щоб громадський конструктор без аргументів мав змогу створювати об'єкт. Для більшості випадків використання конструктор no-arg не є проблемою.

Є хаки на основі серіалізації, які можуть обійти відсутність конструктора без аргументів, оскільки серіалізація використовує jvm магію для створення об'єктів без виклику конструктора. Але це доступно не для всіх віртуальних машин. Наприклад, XStream може створювати екземпляри об'єктів, у яких немає загальнодоступного конструктора no-arg, але лише запускаючи в так званому "розширеному" режимі, який доступний лише для певних віртуальних машин. (Докладні відомості див. За посиланням.) Дизайнери Hibernate, безумовно, вирішили підтримувати сумісність з усіма віртуальними машинами, тому уникають подібних хитрощів, і використовують офіційно підтримуваний метод відображення, що Class<T>.newInstance()вимагає конструктора без аргументів.


31
FYI: Конструктор не повинен бути публічним. Він може мати видимість упаковки і Hibernate повинен setAccessible(true)на ньому.
Grey

Чи можу я створити власний UserType із конструктором, який не є типовим, щоб встановити необхідне поле для його операцій.
L-Samuels

1
Для довідок ObjectInputStreamробить щось за принципами sun.reflect.ReflectionFactory.getReflectionFactory().newConstructorForSerialization(classToGetInstanceOf, Object.class.getConstructor()).newInstance()створення екземплярів об'єктів без конструктора за замовчуванням (JDK1.6 для Windows)
SamYonnou

Re: It can have package visibility and Hibernate should setAccessible(true). Чи itозначає клас, який створюється за допомогою рефлексії? А що Hibernate should setAccessible(true)значить?
Кевін Мередіт

Objenesis робить це і широко використовується багатьма рамками, як весняні дані та mockito github.com/easymock/objenesis
ltfishie

46

Hibernate створює екземпляри ваших об’єктів. Тому вона повинна мати можливість створити їх екземпляр. Якщо немає конструктора no-arg, Hibernate не знатиме, як його створити, тобто який аргумент передавати.

Документація про сплячий режим говорить:

4.1.1. Реалізувати конструктор без аргументів

Усі постійні класи повинні мати конструктор за замовчуванням (який може бути непублічним), щоб Hibernate міг створити їх за допомогою екземпляра Constructor.newInstance(). Рекомендується мати конструктор за замовчуванням з принаймні видимістю пакету для генерації проксі-серверів виконання в режимі глибокого сну.


6
Що стосується видимості конструктора, якщо ви використовуєте JPA v2.0, зверніть увагу, що JSR-317 говорить: Конструктор no-arg повинен бути загальнодоступним або захищеним .
José Andias

@Bozho здрастуйте, сер, у мене є сумніви, що якщо внутрішній сплячий режим використовувати Constructor.newInstance () для інстанції об'єкта, то як перезимувати встановлені значення в поля без визначених задач?
Vikas Verma,

Я не розумію, чому я бачу це застереження щодо неприватного підкласу @Embeddable з публічним конструктором no-arg ...
Amalgovinus

Constructor.newInstance () приймає аргументи, проблема (насправді не-проблема) відображає ці аргументи. Не знаю, чому сплячий режим не вирішив цю проблему. Для порівняння: анотація @JsonCreator у Джексоні робить це, і велика користь від незмінних об'єктів була.
drrob


43

Ем, вибачте всіх, але Hibernate не вимагає, щоб ваші класи мали конструктор без параметрів. Специфікація JPA 2.0 вимагає цього, і це дуже кульгаво від імені JPA. Інші рамки, такі як JAXB, також вимагають цього, що також дуже кульгаво від імені цих фреймворків.

(Насправді, JAXB нібито дозволяє виробляти юридичні заводи, але він наполягає на створенні цих заводів самостійно, вимагаючи, щоб вони мали, - думаю, конструктор без параметрів , який у моїй книзі так само хороший, як не дозволяють заводам; наскільки кульга це !)

Але сплячого не вимагає такого.

Hibernate підтримує механізм перехоплення (див. "Перехоплювач" у документації ), який дозволяє створювати екземпляри об'єктів з будь-якими параметрами конструктора, які їм потрібні.

В основному, ви робите те, що при налаштуванні сплячого режиму ви передаєте йому об'єкт, що реалізує org.hibernate.Interceptorінтерфейс, і в сплячку потім буде застосовуватися instantiate()метод цього інтерфейсу, коли він потребує нового екземпляра вашого об'єкта, тому ваша реалізація цього методу може newваші предмети будь-яким способом вам подобається.

Я зробив це в проекті, і це працює як шарм. У цьому проекті я роблю речі через JPA, коли це можливо, і використовую такі функції Hibernate, як перехоплювач, лише коли у мене немає іншого варіанту.

Hibernate, здається, дещо невпевнений у цьому, оскільки під час запуску він видає інформаційне повідомлення для кожного з моїх класів сутності, повідомляючи мені INFO: HHH000182: No default (no-argument) constructor for classі class must be instantiated by Interceptor, але потім пізніше я створюю їх за допомогою перехоплювача, і це задоволено.

Щоб відповісти на частину запитання "чому" щодо інших інструментів, окрім сплячого режиму , відповідь "абсолютно без поважних причин", і це підтверджується існуванням перехоплювача сплячого режиму. Існує безліч інструментів, які могли б підтримувати якийсь подібний механізм створення клієнтських об’єктів, але вони цього не роблять, тому вони створюють об’єкти самостійно, тому їм потрібні конструктори без параметрів. Мені спокушається вірити, що це відбувається тому, що творці цих інструментів вважають себе програмістами системи ніндзя, які створюють рамки, наповнені магією, для використання необізнаними програмістами програм, які (так вони думають) ніколи в своїх найсміливіших мріях не мали потреба в таких вдосконалених конструкціях, як ... Заводський шаблон . (Добре,так думати. Я насправді так не думаю. Я жартую.)


1
Нарешті, хтось отримує! Я витратив більше свого часу, ніж мені подобається, маючи справу з цими фреймворками, затуляючи процес створення об'єкта (що є надзвичайно важливим для належної ін'єкції залежностей та для багатої поведінки об'єкта). Крім того, відображення Java дозволяє створювати об'єкти без використання newInstance (). Метод getDeclaredConstructors був у API відображення з JDK 1.1. Страшно, що дизайнери специфікацій JPA нехтували цим.
drrob

Це неправильно. Якщо Hibernate використовується як постачальник JPA для наполегливості, він вимагає конструктора за замовчуванням, інакше випливає наступне, Caused by: org.hibernate.InstantiationException: No default constructor for entity: : hibernate.tutorial.Studentщо відбулося нещодавно, тому що javax.persistence.*;використовується, і лише org.hibernateпри створенніSession, SessionFactory, and Configuration
Mushy

2
@Mushy Це цілком правильно, тому що а) питання стосується сплячого режиму, без жодної згадки про JPA, і б) Тим не менше я прямо згадую у другому реченні моєї відповіді, що JPA вимагає конструкторів за замовчуванням, хоча сплячий режим цього не вимагає.
Mike Nakis

36

Зимова сплячка - це структура 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>);

Який конструктор слід викликати в сплячий режим для отримання об’єкта Person? Бачиш ?

І, нарешті, за допомогою відображення Hibernate може створити клас за допомогою свого конструктора no-arg. Тож коли ви телефонуєте

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

Hibernate створить екземпляр вашого об’єкта Person наступним чином

Person.class.newInstance();

Який згідно з документацією API

Клас створюється як екземпляр нового виразу з порожнім списком аргументів

Мораль розповіді

Person.class.newInstance();

схожий на

new Person();

Більш нічого


1
Це, безумовно, найкращий опис, який я знайшов щодо цього питання. Більшість відповідей, які я знайшов, використовували книжкові технічні терміни, і жоден орган не пояснив це надійним способом, як ви. Кудо вам і дякую!
Темний лицар

1
Це, можливо, міркування команди сплячого. Але насправді проблеми можна було б вирішити, (1) вимагаючи або анотації, або, лише використовуючи конструктор, який не використовується за замовчуванням, якщо є лише один конструктор, та (2) використовуючи class.getDeclaredConstructors. І використання Constructor.newInstance () замість Class.newInstance (). До Java 8 потрібне відповідне відображення у XML / анотаціях, але це цілком можливо.
drrob

Гаразд, так сплячий режим створює об’єкт із конструктора за замовчуванням, а потім використовує сетери для полів nameі age? Якщо ні, то згодом він використовує інший конструктор?
tryHard

2
@tryingHard Так, після створення екземпляра Hibernate використовує сетери або поля - це залежить від стратегії доступу. За замовчуванням розміщення анотації Id дає стратегію доступу за замовчуванням. Див. Docs.jboss.org/hibernate/orm/5.1/userguide/html_single/chapters/…
Артур Рональд

6

Насправді ви можете створювати класи, які не мають конструктора 0-args; Ви можете отримати список конструкторів класу, вибрати один і викликати його з фіктивними параметрами.

Хоча це можливо, і, мабуть, це спрацювало б і не було б проблематично, доведеться погодитися, що це досить дивно.

Побудова об'єктів так, як робить Hibernate (я вважаю, що він викликає конструктор 0-arg, а потім, ймовірно, змінює поля екземпляра безпосередньо за допомогою Reflection. Можливо, він знає, як викликати сетерів), дещо суперечить тому, як об'єкт повинен бути побудований в Java - викликайте конструктор з відповідними параметрами, щоб новий об'єкт був потрібним об'єктом. Я вважаю, що інстанціювання об'єкта, а потім його мутування дещо "анти-Java" (або я б сказав, анти-чиста теоретична Java) - і, безумовно, якщо це зробити за допомогою прямого польового маніпулювання, це буде інкапсуляцією і всіма цими фантазійними інкапсуляційними матеріалами .

Я думаю, що правильним способом зробити це було б визначити в картоні сплячого режиму, як об’єкт повинен бути екземпляром з інформації в рядку бази даних, використовуючи відповідний конструктор ... але це було б складніше - це означає, що обидва сплячого стануть навіть складніше, картографування було б складніше ... і все було б "чистішим"; і я не думаю, що це не матиме переваги перед існуючим підходом (окрім того, як почувати себе добре робити справи "належним чином").

Сказавши це, і побачивши, що підхід у сплячому режимі не дуже "чистий", обов'язок мати конструктор 0-arg не є строго необхідним, але я можу дещо зрозуміти вимогу, хоча я вважаю, що вони це зробили чисто "належним чином" "підстави, коли вони відхилялися від" належного способу "(хоча і з розумних причин) задовго до цього.


5

Hibernate потребує створення екземплярів у результаті ваших запитів (за допомогою відображення), Hibernate для цього покладається на конструктор no-arg з сутностями, тому вам потрібно надати конструктор no-arg. Що незрозуміло?


За яких умов privateконструктор невірний? Я бачу java.lang.InstantiationExceptionнавіть із privateконструктором для моєї організації JPA. посилання .
Kevin Meredith

Я спробував клас без порожнього конструктора (але з конструктором args), і він спрацював. Я отримав ІНФО від сплячого режиму "INFO: HHH000182: Немає конструктора за замовчуванням (без аргументів) для класу та класу, який повинен створюватися екземпляром" Перехоплювач ", але не було винятків, і об'єкт був успішно отриманий від БД.
дамб леп

2

Набагато простіше створити об'єкт з безпараметричним конструктором за допомогою відображення, а потім заповнити його властивості даними через відображення, ніж намагатися зіставити дані з довільними параметрами параметризованого конструктора, зі зміною імен / конфліктів імен, невизначеною логікою всередині конструктора, набори параметрів, що не відповідають властивостям об'єкта тощо.

Багато ORM та серіалізатори потребують конструкторів без параметрів, оскільки параметризовані конструктори через відображення дуже крихкі, а конструктори без параметрів забезпечують як стабільність додатку, так і контроль над поведінкою об'єкта розробнику.


Я б стверджував, що примушування до повної мінливості того, що може бути необхідним, щоб бути багатим об'єктом домену, є ще більш крихким (це не є великим ORM, якщо ваші сутності повинні мати безликі пакети даних для роботи - я тут, тому що хочу конструктор, але замість цього має невизначений порядок розширених викликів сетерів) ... Але +1, тому що ви усвідомлюєте, що відображення можна виконати на конструкторах за допомогою аргументів :)
drrob

2

Hibernate використовує проксі для ледачого завантаження. Якщо ви не визначите конструктор або зробите його приватним, деякі речі все одно можуть працювати - ті, які не залежать від механізму проксі. Наприклад, завантаження об’єкта (без конструктора) безпосередньо за допомогою API запиту.

Але якщо ви використовуєте метод session.load (), ви зіткнетеся з InstantiationException з lib генератора проксі-сервера через відсутність конструктора.

Цей хлопець повідомив про подібну ситуацію:

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


0

Перегляньте цей розділ специфікації мови Java, який пояснює різницю між статичними та нестатичними внутрішніми класами: http://java.sun.com/docs/books/jls/third_edition/html/classes.html#8.1.3

Статичний внутрішній клас концептуально не відрізняється від звичайного загального класу, оголошеного у файлі .java.

Оскільки Hibernate повинен створити екземпляр ProjectPK незалежно від екземпляра Project, ProjectPK або повинен бути статичним внутрішнім класом, або оголошений у власному файлі .java.

посилання org.hibernate.InstantiationException: Немає конструктора за замовчуванням

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.