Hibernate neden bağımsız değişken oluşturucu gerektirmez?


106

Bağımsız değişken içermeyen kurucu bir gereksinimdir (Hazırda Bekletme gibi araçlar, nesneleri başlatmak için bu yapıcıdaki yansımayı kullanır).

Bu el dalgalı cevabı aldım ama biri daha fazla açıklayabilir mi? Teşekkürler


7
Bilginize: The no-argument constructor is a requirement Yanlış olan iddia ve bunun gerçekten böyle olup olmadığını sorgulamadan bunun neden böyle olduğunu açıklamaya devam eden tüm yanıtlar (ödül almış olan kabul edilen yanıt dahil) yanlıştır . Bu yanıtı görün: stackoverflow.com/a/29433238/773113
Mike Nakis

2
JPA için bir sağlayıcı olarak hazırda bekletme kullanıyorsanız gereklidir.
Amalgovinus

1
@MikeNakis Yanlış Mike'sın. Hazırda bekletme, JPA (Amalgovinus) için bir sağlayıcı olarak hazırda bekletme kullanıyorsanız, nesneleri başlatmak için varsayılan bir kurucu gerektirir, aksi takdirde Hazırda Bekletme, Caused by: org.hibernate.InstantiationException: No default constructor for entity: : hibernate.tutorial.Studentaz önce karşılaştığım durumda olduğu gibi rapor verecektir
Mushy

@Mushy soru "hazırda beklet" ve "orm" ile etiketlenmiş, "jpa" ile etiketlenmemiş. Soruda JPA'dan bahsedilmiyor.
Mike Nakis

1
@MikeNakis Mike'ı kabul ediyorum ancak Hibernate "JPA" nın bir uygulaması olarak kullanılıyor ve "JPA" veya "ORM" yokluğunda kullanılmıyor. Bu nedenle, varsayım hazırda bekletme "JPA" uygulamaktır.
Mushy

Yanıtlar:


139

Hazırda bekletme ve genel olarak yansıtma yoluyla nesneler oluşturan kod Class<T>.newInstance(), sınıflarınızın yeni bir örneğini oluşturmak için kullanın . Bu yöntem, nesneyi somutlaştırabilmek için genel arginsiz bir kurucu gerektirir. Çoğu kullanım durumu için, argüman içermeyen bir kurucu sağlamak bir sorun değildir.

Serileştirme, yapıcıyı çağırmadan nesneler oluşturmak için jvm sihrini kullandığından, arginsiz bir yapıcıya sahip olmama konusunda işe yarayabilecek serileştirmeye dayalı hackler vardır. Ancak bu, tüm VM'lerde mevcut değildir. Örneğin, XStream , arginsiz bir genel yapıcıya sahip olmayan, ancak yalnızca belirli VM'lerde kullanılabilen sözde "gelişmiş" modda çalışarak nesnelerin örneklerini oluşturabilir. (Ayrıntılar için bağlantıya bakın.) Hibernate tasarımcıları kesinlikle tüm VM'lerle uyumluluğu korumayı seçti ve bu nedenle bu tür hilelerden kaçınıyor ve Class<T>.newInstance()argümansız bir kurucu gerektiren resmi olarak desteklenen yansıtma yöntemini kullanıyor .


31
Bilginize: Oluşturucunun herkese açık olması gerekmez. Paket görünürlüğüne sahip olabilir ve üzerinde Hazırda Bekletme olmalıdır setAccessible(true).
Grey

İşlemleri için gerekli bir alanı ayarlamak için varsayılan olmayan bir kurucu ile bir Özel Kullanıcı Tipi oluşturabilir miyim?
L-Samuels

1
Referans için ObjectInputStream, sun.reflect.ReflectionFactory.getReflectionFactory().newConstructorForSerialization(classToGetInstanceOf, Object.class.getConstructor()).newInstance()varsayılan kurucu olmayan nesnelerin örneklenmesi için satır boyunca bir şey yapar (Windows için
JDK1.6

re: It can have package visibility and Hibernate should setAccessible(true). itYansıma yoluyla sınıfın somutlaştırıldığı anlamına mı geliyor? Ve ne anlama Hibernate should setAccessible(true)geliyor?
Kevin Meredith

Objenesis bunu yapar ve yay-verisi ve mockito gibi pek çok çerçeve tarafından yaygın olarak kullanılmaktadır github.com/easymock/objenesis
ltfishie

47

Hazırda bekletme, nesnelerinizi somutlaştırır. Bu yüzden onları somutlaştırabilmesi gerekiyor. Eğer argümansız bir kurucu yoksa, Hibernate bunu nasıl başlatacağını , yani hangi argümanın geçeceğini bilemez .

Derin uyku belgelerine diyor ki:

4.1.1. Bağımsız değişken içermeyen bir kurucu uygulayın

Tüm kalıcı sınıfların bir varsayılan kurucuya (genel olmayan olabilir) sahip olması gerekir, böylece Hibernate bunları kullanarak başlatabilir Constructor.newInstance(). Hazırda Bekletme'de çalışma zamanı proxy üretimi için en azından paket görünürlüğüne sahip varsayılan bir kurucunuz olması önerilir.


6
Yapıcı görünürlüğüne gelince, JPA v2.0 kullanıyorsanız JSR-317'nin şunu söylediğine dikkat edin: no-arg kurucusu herkese açık veya korumalı olmalıdır .
José Andias

@Bozho merhaba efendim, nesneyi başlatmak için dahili olarak hazırda bekletme durumunda Constructor.newInstance () 'ı kullanırsanız, herhangi bir ayarlayıcı tanımlanmadan set değerlerini alanlara nasıl hazırda bekleteceğinizden bir şüphem var?
Vikas Verma

Herkese açık tartışmasız bir kurucuya sahip @ Gömülebilir özel olmayan bir alt sınıf için bu uyarıyı neden gördüğümü anlamıyorum ...
Amalgovinus

Constructor.newInstance () argümanları alıyor, mesele (aslında sorun değil) bu argümanları eşliyor. Hazırda bekletmenin bu sorunu neden çözmediği hakkında hiçbir fikrim yok. Karşılaştırma için: Jackson'daki @JsonCreator ek açıklaması bunu yapıyor ve değişmez nesnelerin büyük yararı vardı.
drrob


45

Erm, üzgün herkes, ama hazırda yok değil dersleriniz parametresiz yapıcı olması gerekir gerektirir. JPA 2.0 spesifikasyonu bunu gerektirir ve bu JPA adına çok saçma. JAXB gibi diğer çerçeveler de bunu gerektirir, bu da bu çerçeveler adına çok zayıftır.

(Aslında, JAXB sözde varlık fabrikalarına izin veriyor, ancak bu fabrikaları kendi başına somutlaştırmakta ısrar ediyor, onların - tahmin et ne oldu - parametresiz kurucuya sahip olmalarını gerektiriyor ki bu benim kitabımda tam olarak fabrikalara izin vermemek kadar iyidir; bu ne kadar kötü? !)

Ancak Hibernate böyle bir şeye ihtiyaç duymaz.

Hazırda bekletme, nesnelerinizi ihtiyaç duydukları kurucu parametreleriyle başlatmanıza olanak sağlayan bir durdurma mekanizmasını destekler ( belgelerdeki "Durdurucu" bölümüne bakın ).

Temel olarak, yaptığınız şey, hazırda bekletme modunu kurduğunuzda, org.hibernate.Interceptorarabirimi uygulayan bir nesneyi iletirsiniz ve hazırda bekletme instantiate(), sizin bir nesnenizin yeni bir örneğine ihtiyaç duyduğunda o arabirimin yöntemini çağırır, böylece bu yöntemi uygulamanız newnesnelerinizi istediğiniz şekilde.

Bunu bir projede yaptım ve bir cazibe gibi çalışıyor. Bu projede mümkün olduğunca JPA üzerinden bir şeyler yapıyorum ve sadece başka seçeneğim olmadığında önleme gibi Hazırda Bekletme özelliklerini kullanıyorum.

Hazırda bekletme bu konuda biraz güvensiz görünüyor, çünkü başlangıç ​​sırasında her varlık sınıfım için bir bilgi mesajı yayınlıyor, bana INFO: HHH000182: No default (no-argument) constructor for classve class must be instantiated by Interceptordiyor, ancak daha sonra onları durdurucu tarafından başlatıyorum ve bundan memnunum.

Hazırda Bekletme dışındaki araçlar için sorunun "neden" bölümünü yanıtlamak için , yanıt "kesinlikle iyi bir neden yok" olur ve bu, hazırda bekletme önleyicisinin varlığıyla kanıtlanmıştır. Orada, istemci nesne somutlaştırması için bazı benzer mekanizmaları desteklemiş olabilecek birçok araç var, ama yapmıyorlar, bu yüzden nesneleri kendileri yaratıyorlar, bu yüzden parametresiz kuruculara ihtiyaç duymaları gerekiyor. Bunun gerçekleştiğine inanmak için baştan çıkarıyorum çünkü bu araçların yaratıcıları kendilerini cahil uygulama programcıları tarafından kullanılmak üzere sihirle dolu çerçeveler yaratan ninja sistem programcıları olarak görüyorlar (bu yüzden düşündükleri) en çılgın hayallerinde asla Fabrika Modeli gibi gelişmiş yapılara ihtiyaç var . (Tamam,öyle düşünmek. Ben yok aslında öyle düşünüyorum. Şaka yapıyorum.)


1
Sonunda, onu anlayan biri! Zamanımın çoğunu, nesne somutlaştırma sürecini gizleyen bu çerçevelerle uğraşmaktan daha fazla harcadım (bu, uygun bağımlılık enjeksiyonu ve zengin nesne davranışı için kesinlikle çok önemlidir). Ayrıca, Java yansıması newInstance () kullanmadan nesneler oluşturmanıza izin verir. GetDeclaredConstructors yöntemi JDK 1.1'den beri yansıma API'sindedir. JPA özellik tasarımcılarının bunu ihmal etmesi korkutucu.
drrob

Bu yanlış. Hazırda bekletme, kalıcılık için JPA sağlayıcısı olarak kullanılıyorsa, varsayılan bir kurucu gerektirir, aksi takdirde, Caused by: org.hibernate.InstantiationException: No default constructor for entity: : hibernate.tutorial.Studentkısa süre önce javax.persistence.*;kullanıldığı için ve yalnızca org.hibernateSession, SessionFactory, and Configuration
Mushy

2
@Mushy Bu tamamen doğrudur, çünkü a) soru tek bir JPA bahsetmeden hazırda bekletme ile ilgili ve b) Yine de cevabımın ikinci cümlesinde, hazırda bekletme olmasa bile JPA'nın varsayılan kurucular gerektirdiğini açıkça belirtiyorum.
Mike Nakis

36

Hazırda bekletme, alan veya mülk erişim stratejisini destekleyen bir ORM çerçevesidir. Ancak, yapıcı tabanlı haritalamayı desteklemiyor - belki ne istersiniz? - gibi bazı sorunlar nedeniyle

Sınıfınız çok sayıda kurucu içeriyorsa ne olur?

public class Person {

    private String name;
    private Integer age;

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

}

Gördüğünüz gibi, bir tutarsızlık sorunuyla uğraşıyorsunuz çünkü Hibernate hangi kurucunun çağrılması gerektiğini varsayamıyor. Örneğin, depolanan bir Person nesnesini almanız gerektiğini varsayalım.

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

Person nesnesini almak için hangi kurucu Hazırda Bekletme çağrısı yapmalıdır? Görebiliyor musun ?

Ve son olarak, yansımayı kullanarak, Hibernate argümansız kurucusu aracılığıyla bir sınıfı başlatabilir. Yani aradığında

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

Hazırda bekletme, Kişi nesnenizi aşağıdaki gibi başlatacaktır

Person.class.newInstance();

API belgelerine göre hangisi

Sınıf, boş bir bağımsız değişken listesi olan yeni bir ifade ile başlatılır.

Hikayeden çıkarılacak ders

Person.class.newInstance();

benzer

new Person();

Başka hiçbir şey


1
Bu, bu soruyla ilgili bulduğum en mükemmel tanımdır. Bulduğum cevapların çoğu kitap gibi teknik terimler kullanılmış ve hiçbir kimse bunu sizin yaptığınız gibi esnek bir şekilde açıklamadı. Tebrikler ve teşekkürler!
Kara Şövalye

1
Bu, Hazırda Bekletme ekibinin mantığı olabilir. Ancak gerçekte, sorunlar (1) ya bir ek açıklama gerektirerek ya da yalnızca bir yapıcı varsa varsayılan olmayan bir kurucu kullanarak ve (2) class.getDeclaredConstructors kullanarak çözülebilir. Ve Class.newInstance () yerine Constructor.newInstance () kullanarak. Java 8'den önce XML / ek açıklamalarda uygun eşleştirme gerekli olacaktır, ancak bu tamamen yapılabilir.
drrob

Tamam, bu nedenle hazırda bekletme varsayılan kurucudan nesne oluşturur ve sonra alanlar için ayarlayıcıları kullanır nameve age? Değilse, daha sonra başka bir kurucu kullanıyor mu?
tryingHard

2
@tryingHard Evet, bir kez başlatıldığında, Hazırda Bekletme ayarlayıcıları veya alanları kullanır - Erişim stratejisine bağlıdır. Varsayılan olarak, Kimlik ek açıklamasının yerleştirilmesi, varsayılan erişim stratejisini verir. Bkz. Docs.jboss.org/hibernate/orm/5.1/userguide/html_single/chapters/…
Arthur Ronald

6

Aslında, 0-args yapıcısı olmayan sınıfları başlatabilirsiniz; bir sınıf kurucularının bir listesini alabilir, birini seçebilir ve sahte parametrelerle çağırabilirsiniz.

Bu mümkün olsa da, işe yarayacak ve sorunlu olmayacak olsa da, bunun oldukça tuhaf olduğunu kabul etmeniz gerekecek.

Nesneleri Hibernate'in yaptığı gibi inşa etmek (0-arg kurucusunu çağırdığına inanıyorum ve sonra muhtemelen örneğin alanlarını doğrudan Yansıma yoluyla değiştiriyor. Belki de ayarlayıcıları nasıl çağıracağını biliyor) bir nesnenin nasıl inşa edilmesi gerektiğine biraz ters gidiyor Yeni nesnenin istediğiniz nesne olması için oluşturucuyu uygun parametrelerle Java ile çağırın. Bir nesneyi örneklemenin ve sonra onu mutasyona uğratmanın bir şekilde "Java karşıtı" olduğuna inanıyorum (veya diyebilirim ki, anti-saf teorik Java) - ve kesinlikle, bunu doğrudan alan manipülasyonu yoluyla yaparsanız, kapsülleme ve tüm bu süslü kapsülleme şeylerine gider .

Bunu yapmanın uygun yolunun, Hazırda Bekletme eşlemesinde bir nesnenin doğru kurucu kullanılarak veritabanı satırındaki bilgilerden nasıl başlatılacağını tanımlamak olacağını düşünüyorum ... ancak bu daha karmaşık olacaktır - yani her iki Hazırda Bekletme eşit olacaktır. daha karmaşık, haritalama daha karmaşık olacaktır ... ve hepsi daha "saf" olacaktır; ve bunun mevcut yaklaşıma göre bir avantajı olacağını düşünmüyorum (işleri "doğru şekilde" yapma konusunda iyi hissetmek dışında).

Bunu söyledikten sonra ve Hibernate yaklaşımının çok "temiz" olmadığını görünce, 0-arg oluşturucuya sahip olma yükümlülüğü kesinlikle gerekli değildir, ancak bunu tamamen "doğru bir şekilde yaptıklarına inanıyorum, ancak gereksinimi biraz anlayabiliyorum "uygun yoldan" (makul nedenlerle de olsa) çok daha önce saptıkları zaman.


5

Hibernate'in sorgularınızın sonucu olarak (yansıtma yoluyla) örnekler oluşturması gerekir, Hibernate bunun için varlıkların argüman içermeyen yapıcısına güvenir, bu nedenle argüman içermeyen bir kurucu sağlamanız gerekir. Açık olmayan ne?


Bir privatekurucu hangi koşullar altında yanlıştır? JPA varlığım için java.lang.InstantiationExceptionbir privatekurucu ile bile görüyorum . referans .
Kevin Meredith

Sınıfı boş yapıcı olmadan denedim (ancak args kurucusu ile) ve işe yaradı. Hazırda bekletme "INFO: HHH000182: Sınıf ve sınıf için hiçbir varsayılan (bağımsız değişken) kurucu, Interceptor tarafından başlatılmamalıdır", ancak istisna yoktu ve nesne DB'den başarıyla alındı.
lijep barajı

2

Yansıtma yoluyla parametresiz bir kurucu ile nesne oluşturmak ve daha sonra özelliklerini yansıtma yoluyla verilerle doldurmak, verileri parametreli bir kurucunun keyfi parametreleriyle, değişen adlar / adlandırma çakışmaları, yapıcı içindeki tanımsız mantıkla denemek ve eşleştirmekten çok daha kolaydır. parametre kümeleri bir nesnenin özellikleriyle eşleşmiyor, vb.

Birçok ORM ve serileştirici parametresiz oluşturucular gerektirir, çünkü yansıtma yoluyla paramterize oluşturucular çok kırılgandır ve parametresiz oluşturucular hem uygulamaya kararlılık hem de geliştiriciye nesne davranışı üzerinde kontrol sağlar.


Zengin bir etki alanı nesnesi olması gerekebilecek şey üzerinde tam değişkenliği zorlamanın daha da kırılgan olduğunu iddia ediyorum (varlıklarınızın çalışması için özelliksiz veri paketleri olması gerekiyorsa, bu çok fazla bir ORM değildir - buradayım çünkü istiyorum bir kurucu, ancak bunun yerine tanımlanmamış bir zengin setter çağrıları sırası var) ... Ama +1 çünkü yansımanın
bağımsız değişkenli kurucularda

2

Hibernate, geç yükleme için proxy'ler kullanır. Bir kurucu tanımlamazsanız veya onu özel yapmazsanız, proxy mekanizmasına bağlı olmayan birkaç şey hala işe yarayabilir. Örneğin, nesneyi (yapıcı olmadan) doğrudan sorgu API'si kullanarak yükleme.

Ancak, session.load yöntemini () kullanırsanız, yapıcının kullanılabilir olmaması nedeniyle proxy oluşturucu lib'den InstantiationException ile karşılaşırsınız.

Bu adam benzer bir durum bildirdi:

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


0

Statik ve statik olmayan iç sınıflar arasındaki farkı açıklayan Java dil spesifikasyonunun bu bölümüne bakın: http://java.sun.com/docs/books/jls/third_edition/html/classes.html#8.1.3

Statik bir iç sınıf, kavramsal olarak bir .java dosyasında bildirilen normal bir genel sınıftan farklı değildir.

Hibernate'in Project örneğinden bağımsız olarak ProjectPK'yi başlatması gerektiğinden, ProjectPK'nin statik bir iç sınıf olması veya kendi .java dosyasında bildirilmesi gerekir.

başvuru org.hibernate.InstantiationException: Varsayılan yapıcı yok


0

Hibernate, genellikle çağırdığınızda get()veya load()yöntemleri kullandığınızda, Entity fasulye örneğini oluşturmak için Reflection API kullanır . Yöntem Class.newInstance()bunun için kullanılır ve no-argskurucu gerektirir . Bu nedenle, varlık fasulye'lerinde args kurucunuz yoksa, hazırda bekletme bunu başlatamaz ve HibernateException alırsınız.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.