Bu soru biraz Hazırda Bekletme Açıklama Yerleştirme Sorusu ile ilgilidir .
Ama hangisinin daha iyi olduğunu bilmek istiyorum ? Mülkler üzerinden mi, alanlar üzerinden erişim mi? Her birinin avantajları ve dezavantajları nelerdir?
Bu soru biraz Hazırda Bekletme Açıklama Yerleştirme Sorusu ile ilgilidir .
Ama hangisinin daha iyi olduğunu bilmek istiyorum ? Mülkler üzerinden mi, alanlar üzerinden erişim mi? Her birinin avantajları ve dezavantajları nelerdir?
Yanıtlar:
İhtiyacım olduğunda erişimcilerime iş mantığı ekleyebileceğim için erişimcileri tercih ederim. İşte bir örnek:
@Entity
public class Person {
@Column("nickName")
public String getNickName(){
if(this.name != null) return generateFunnyNick(this.name);
else return "John Doe";
}
}
Ayrıca, karışıma başka bir libs atıyorsanız (bazı JSON dönüştüren lib veya BeanMapper veya Dozer veya alıcı / ayarlayıcı özelliklerine dayalı diğer fasulye haritalama / klonlama lib gibi), lib'in kalıcılıkla senkronize olduğunu garanti edersiniz yöneticisi (her ikisi de alıcı / ayarlayıcı kullanın).
Her ikisi için de argümanlar vardır, ancak bunların çoğu "kullanıcı için mantık eklemeniz gerekiyorsa" veya "xxxx enkapsülasyonu keser" gibi belirli kullanıcı gereksinimlerinden kaynaklanır. Bununla birlikte, kimse teori hakkında gerçekten yorum yapmadı ve uygun bir şekilde gerekçelendirildi.
Hazırda Beklet / JPA bir nesneyi sürdürdüğünde gerçekte ne yapar - şey, nesnenin DEVLETİNİ devam ettirir. Bu, kolayca çoğaltılabilecek şekilde saklanması anlamına gelir.
Kapsülleme nedir? Kapsülleme, verilerin (veya durumun) uygulamanın / istemcinin verilere güvenli bir şekilde erişmek için kullanabileceği bir arayüzle kapsüllenmesi anlamına gelir - tutarlı ve geçerli tutar.
Bunu MS Word gibi düşünün. MS Word bellekte belgenin bir modelini (STATE belgeleri) korur. Kullanıcının belgeyi değiştirmek için kullanabileceği bir arabirim sunar - bir dizi düğme, araç, klavye komutları vb. Ancak, bu belgeyi sürdürmeyi (Kaydetmeyi) seçtiğinizde, tuş basışı kümesini değil dahili durumu kaydeder ve fare tıklamaları oluşturmak için kullanılır.
Nesnenin dahili durumunu kaydetmek kapsüllemeyi KESİNMEZ - aksi takdirde kapsüllemenin ne anlama geldiğini ve neden var olduğunu gerçekten anlamıyorsunuz. Gerçekten nesne serileştirmesi gibidir.
Bu nedenle, ÇOK DURUMDA, AKSESUARLAR yerine ALANLARA devam etmek uygundur. Bu, bir nesnenin tam olarak depolandığı şekilde veritabanından yeniden oluşturulabileceği anlamına gelir. Herhangi bir validasyona gerek yoktur, çünkü bu orijinalin yaratıldığı zaman ve veritabanında saklanmadan önce yapıldı (Tanrı yasakladığı sürece, DB'de geçersiz veriler depolamıyorsunuz !!!!). Benzer şekilde, nesne saklanmadan önce hesaplandıkları için değerlerin hesaplanmasına gerek yoktur. Nesne kaydedilmeden önceki gibi görünmelidir. Aslında, alıcılara / ayarlayıcılara ek şeyler ekleyerek , orijinalin tam bir kopyası olmayan bir şeyi yeniden oluşturma riskinizi artırabilirsiniz .
Tabii ki, bu işlevsellik bir nedenden dolayı eklenmiştir. Erişimcileri devam ettirmek için bazı geçerli kullanım durumları olabilir, ancak bunlar tipik olarak nadirdir. Bir örnek, hesaplanan bir değerin devam etmesini önlemek isteyebilir, ancak değerin alıcısında neden talep üzerine hesaplayamadığınızı sormak veya alıcıda tembel bir şekilde başlatmak istemeyebilirsiniz. Şahsen iyi bir kullanım durumu düşünemiyorum ve buradaki cevapların hiçbiri gerçekten bir "Yazılım Mühendisliği" yanıtı vermiyor.
Ben alan erişimi tercih, çünkü bu şekilde her özellik için alıcı / ayarlayıcı sağlamak zorunda değilim.
Google aracılığıyla yapılan hızlı bir anket, saha erişiminin çoğunluk olduğunu gösteriyor (ör. Http://java.dzone.com/tips/12-feb-jpa-20-why-accesstype . ).
Alan erişiminin Spring tarafından önerilen deyim olduğuna inanıyorum, ancak bunu destekleyecek bir referans bulamıyorum.
Bir var , ilgili SO soru performansını ölçmek için çalıştı ve "hayır fark var" sonucuna vardı.
İşte emlak erişimcilerini kullanmak zorunda olduğunuz bir durum. 8 somut alt sınıfa miras almak için birçok uygulama iyiliği olan bir GENERIC özet sınıfınız olduğunu düşünün:
public abstract class Foo<T extends Bar> {
T oneThing;
T anotherThing;
// getters and setters ommited for brevity
// Lots and lots of implementation regarding oneThing and anotherThing here
}
Şimdi bu sınıfa nasıl açıklama eklemelisiniz? Cevap, bu noktada hedef varlığı belirleyemediğiniz için alan veya mülk erişimi ile hiçbir şekilde açıklama ekleyemeyeceğinizdir. Somut uygulamalara açıklama eklemeniz GEREKİR. Ancak kalıcı özellikler bu üst sınıfta bildirildiğinden, alt sınıflarda özellik erişimini KULLANMALISINIZ.
Soyut genel süper sınıfları olan bir uygulamada alan erişimi bir seçenek değildir.
abstract T getOneThing()
ve abstract void setOneThing(T thing)
ve saha erişimini kullanabilir.
Mülk erişimcilerini tercih etme ve kullanma eğilimindeyim:
foo.getId()
bir proxy başlatmadan aramamı sağlar (Hazırda Beklet'i kullanırken, HHH-3718 çözülene kadar önemlidir ).dezavantajı:
@Transient
.Bu gerçekten belirli bir duruma bağlıdır - her iki seçenek de bir nedenle kullanılabilir. IMO üç vakaya kadar kaynar:
Ayarlayıcılarda sadece değeri ayarlamaktan daha fazla bir şey yapmak istiyorsanız, alan erişimi ve NOT eklentileri (özellik erişimi) şiddetle tavsiye ediyorum (örn. Şifreleme veya hesaplama).
Özellik erişimindeki sorun, nesne yüklendiğinde ayarlayıcıların da çağrılmasıdır. Şifrelemeyi tanıtmak isteyene kadar bu benim için aylarca işe yaradı. Kullanım durumumuzda ayarlayıcıdaki bir alanı şifrelemek ve alıcıda şifresini çözmek istedik. Özellik erişimiyle ilgili sorun, Hazırda Bekletme nesneyi yüklediğinde, alanı doldurmak için ayarlayıcıyı çağırdığı ve böylece şifrelenmiş değeri tekrar şifrelediği idi. Bu yazı ayrıca şunu da belirtir: Java Hazırda Bekletme: Kimi çağırdığına bağlı olarak farklı özellik kümesi işlev davranışı
Bu, saha erişimi ile mülk erişimi arasındaki farkı hatırlayana kadar başım ağrıyor. Şimdi tüm ek açıklamalarımı mülk erişiminden saha erişimine taşıdım ve şimdi iyi çalışıyor.
Saha erişimini aşağıdaki nedenlerle kullanmayı tercih ederim:
Mülkiyet erişim eşittir / hashCode uygulanması ve zaman çok kötü hatalar yol açabilir doğrudan alanlara başvuran (kendi alıcılar aracılığıyla aksine). Bunun nedeni, proxy'nin yalnızca alıcılara erişildiğinde başlatılması ve doğrudan alan erişiminin null değerini döndürmesidir.
Mülkiyet erişim tüm açıklama gerektirir yarar yöntemleri olarak (örneğin addChild / removeChild) @Transient
.
Alan erişimi sayesinde, alıcıyı göstermeyecek şekilde @ Sürüm alanını gizleyebiliriz. Bir alıcı da bir ayarlayıcı eklemeye yol açabilir ve version
alan asla manuel olarak ayarlanmamalıdır (bu çok kötü sorunlara yol açabilir). Tüm sürüm artışı, OPTIMISTIC_FORCE_INCREMENT veya PESSIMISTIC_FORCE_INCREMENT açık kilitleme yoluyla tetiklenmelidir .
version
Alan için erişimcilerin teşhir edilmesi, müstakil varlıklar yerine DTO'ların kullanıldığı durumlarda genellikle yararlıdır.
Ben ORM bunu yaparsanız bile, alanların güncellenmesi doğrudan kapsülleme tatili nedeniyle özelliği ek açıklama daha iyi olduğunu düşünüyorum.
İşte sizi nerede yakacağına dair harika bir örnek: muhtemelen hazırda bekletme doğrulayıcısı ve kalıcılığı için ek açıklamalarınızı aynı yerde (alanlar veya özellikler) istersiniz. Bir alana eklenmiş olan hazırda bekletme modundan onay alan destekli doğrulamalarınızı test etmek istiyorsanız, birim testinizi yalnızca doğrulayıcıya ayırmak için kuruluşunuzun bir kısmını kullanamazsınız. Ahh.
Tembel başlatma konusunda mülk erişimi ve alan erişimi arasında oldukça farklı olduğuna inanıyorum.
2 temel fasulye için aşağıdaki eşlemeleri göz önünde bulundurun:
<hibernate-mapping package="org.nkl.model" default-access="field">
<class name="FieldBean" table="FIELD_BEAN">
<id name="id">
<generator class="sequence" />
</id>
<property name="message" />
</class>
</hibernate-mapping>
<hibernate-mapping package="org.nkl.model" default-access="property">
<class name="PropBean" table="PROP_BEAN">
<id name="id">
<generator class="sequence" />
</id>
<property name="message" />
</class>
</hibernate-mapping>
Ve aşağıdaki birim testleri:
@Test
public void testFieldBean() {
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
FieldBean fb = new FieldBean("field");
Long id = (Long) session.save(fb);
tx.commit();
session.close();
session = sessionFactory.openSession();
tx = session.beginTransaction();
fb = (FieldBean) session.load(FieldBean.class, id);
System.out.println(fb.getId());
tx.commit();
session.close();
}
@Test
public void testPropBean() {
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
PropBean pb = new PropBean("prop");
Long id = (Long) session.save(pb);
tx.commit();
session.close();
session = sessionFactory.openSession();
tx = session.beginTransaction();
pb = (PropBean) session.load(PropBean.class, id);
System.out.println(pb.getId());
tx.commit();
session.close();
}
Gerekli seçimlerdeki ince farkı göreceksiniz:
Hibernate:
call next value for hibernate_sequence
Hibernate:
insert
into
FIELD_BEAN
(message, id)
values
(?, ?)
Hibernate:
select
fieldbean0_.id as id1_0_,
fieldbean0_.message as message1_0_
from
FIELD_BEAN fieldbean0_
where
fieldbean0_.id=?
0
Hibernate:
call next value for hibernate_sequence
Hibernate:
insert
into
PROP_BEAN
(message, id)
values
(?, ?)
1
Yani, arama fb.getId()
bir seçim gerektirir, oysa pb.getId()
istemez.
Alan tabanlı erişimi seçmenin en önemli nedenlerini özetlemeye çalışayım. Daha derinlemesine dalmak istiyorsanız lütfen blogumdaki şu makaleyi okuyun: JPA ve Hibernate'de Erişim Stratejileri - Hangisi daha iyi, alan veya mülk erişimi?
Alan tabanlı erişim çok daha iyi bir seçenektir. İşte 5 nedeni:
Sebep 1: Kodunuzun daha iyi okunabilirliği
Alan tabanlı erişim kullanıyorsanız, varlık özelliklerinize eşleme ek açıklamalarınızla açıklama eklersiniz. Tüm varlık özniteliklerinin tanımını sınıfınızın en üstüne yerleştirerek, tüm özniteliklerin ve eşlemelerinin nispeten küçük bir görünümünü elde edersiniz.
Sebep 2: Başvurunuz tarafından çağrılmaması gereken alıcı veya ayarlayıcı yöntemlerini atlayın
Alan tabanlı erişimin bir diğer avantajı, kalıcılık sağlayıcınızın (örneğin, Hazırda Bekletme veya EclipseLink) varlık özelliklerinizin alıcı ve ayarlayıcı yöntemlerini kullanmamasıdır. Bu, işletme kodunuz tarafından kullanılmaması gereken herhangi bir yöntem sağlamanız gerekmediği anlamına gelir. Bu çoğunlukla, oluşturulan birincil anahtar niteliklerinin veya sürüm sütunlarının ayarlayıcı yöntemleri için geçerlidir . Kalıcılık sağlayıcınız bu niteliklerin değerlerini yönetir ve bunları programlı olarak ayarlamamanız gerekir.
Sebep 3: Alıcı ve ayarlayıcı yöntemlerinin esnek uygulanması
Kalıcı sağlayıcınız alıcı ve ayarlayıcı yöntemlerini çağırmadığından, herhangi bir dış gereksinimi yerine getirmek zorunda değildir. Bu yöntemleri istediğiniz şekilde uygulayabilirsiniz. Bu, işletmeye özgü doğrulama kuralları uygulamanızı, ek iş mantığını tetiklemenizi veya varlık özelliğini farklı bir veri türüne dönüştürmenizi sağlar.
Örneğin, bunu isteğe bağlı bir ilişkilendirmeyi veya özniteliği bir Java'ya sarmak için kullanabilirsiniz .Optional
Sebep 4: Fayda yöntemlerini @Transient
Alan tabanlı erişim stratejisinin bir diğer yararı da, yardımcı yöntemlerinize açıklama eklemenize gerek olmamasıdır @Transient
. Bu ek açıklama, kalıcılık sağlayıcınıza bir yöntemin veya niteliğin varlık kalıcı durumunun bir parçası olmadığını bildirir. Ve alan tipi erişim ile kalıcı durum varlığınızın nitelikleri tarafından tanımlandığından, JPA uygulamanız varlığınızın tüm yöntemlerini yok sayar.
Sebep 5: Proxy'lerle çalışırken hatalardan kaçının
Hazırda Beklet, tembel olarak bire birleştirilmiş dernekler için proxy kullanır, böylece bu derneklerin başlatılmasını kontrol edebilir. Bu yaklaşım neredeyse tüm durumlarda işe yarar. Ancak mülke dayalı erişim kullanırsanız tehlikeli bir tuzak ortaya çıkarır.
Özellik tabanlı erişim kullanıyorsanız, Hazırda Beklet, getter yöntemini çağırdığınızda proxy nesnesinin niteliklerini başlatır. İş kodunuzda proxy nesnesini kullanıyorsanız her zaman böyle olur. Ancak oldukça eşittir ve hashCode uygulamaları özniteliklere doğrudan erişir. Proxy özniteliklerinden herhangi birine ilk kez erişiyorsanız, bu öznitelikler yine de başlatılmaz.
Varsayılan olarak, JPA sağlayıcıları varlık alanlarının değerlerine erişir ve varlığın JavaBean özellik erişimcisi (alıcı) ve mutator (ayarlayıcı) yöntemlerini kullanarak bu alanları veritabanı sütunlarıyla eşleştirir. Bu nedenle, bir kuruluştaki özel alanların adları ve türleri JPA için önemli değildir. Bunun yerine, JPA yalnızca JavaBean özellik erişimcilerinin adlarına ve dönüş türlerine bakar. Bunu @javax.persistence.Access
, JPA sağlayıcının kullanması gereken erişim yöntemini açıkça belirtmenizi sağlayan ek açıklamayı kullanarak değiştirebilirsiniz .
@Entity
@Access(AccessType.FIELD)
public class SomeEntity implements Serializable
{
...
}
AccessType enum için kullanılabilir seçenekler PROPERTY (varsayılan) ve FIELD'dir. PROPERTY ile, sağlayıcı JavaBean özellik yöntemlerini kullanarak alan değerlerini alır ve ayarlar. FIELD, sağlayıcının örnek alanlarını kullanarak alan değerlerini almasını ve ayarlamasını sağlar. En iyi uygulama olarak, varsayılana sadık kalmanız ve aksi takdirde zorlayıcı bir nedeniniz olmadıkça JavaBean özelliklerini kullanmanız gerekir.
Bu özellik ek açıklamalarını özel alanlara veya genel erişimci yöntemlerine koyabilirsiniz. AccessType.PROPERTY
JavaBean erişimcileri yerine (varsayılan) kullanır ve özel alanlara açıklama eklerseniz , alan adlarının JavaBean özellik adlarıyla eşleşmesi gerekir. Ancak, JavaBean erişimcilerine ek açıklama eklerseniz adların eşleşmesi gerekmez. Benzer şekilde, AccessType.FIELD
alanlar yerine JavaBean erişimcilerini kullanır ve ek açıklama eklerseniz , alan adlarının da JavaBean özellik adlarıyla eşleşmesi gerekir. Bu durumda, alanlara ek açıklama eklerseniz eşleşmeleri gerekmez. JavaBean erişimcilerinin AccessType.PROPERTY
ve alanlarının
tutarlı olması ve açıklama eklenmesi en iyisidir AccessType.FIELD
.
Aynı varlıktaki JPA özellik ek açıklamalarını ve JPA alan ek açıklamalarını asla karıştırmamanız önemlidir. Bunu yapmak belirtilmemiş davranışlarla sonuçlanır ve büyük olasılıkla hatalara neden olur.
Bu eski bir sunumdur ancak Rod, mülk erişimine ilişkin ek açıklamaların anemik alan modellerini teşvik ettiğini ve ek açıklama için "varsayılan" yol olmaması gerektiğini önermektedir.
Alan erişimi lehine bir başka nokta, aksi takdirde koleksiyonerler için ayarlayıcıları açığa çıkarmak zorunda kalmanızdır, çünkü benim için, kalıcı toplama örneğini Hibernate tarafından yönetilmeyen bir nesneye değiştirmek, veri tutarlılığınızı kesinlikle kıracaktır.
Bu nedenle, varsayılan kurucudaki boş uygulamalara başlatılan korumalı alanlar olarak koleksiyonlara sahip olmayı ve yalnızca alıcılarını göstermeyi tercih ederim. Gibi Sonra, yalnızca yönetilen operasyonlar clear()
, remove()
, removeAll()
vb değişikliklerin hazırda habersiz hale asla mümkündür.
Tarlaları tercih ederim, ancak ek açıklamaları alıcılara yerleştirmeye zorlayan bir durumla karşılaştım.
Hazırda Bekletme JPA uygulamasıyla, @Embedded
alanlar üzerinde çalışmaz gibi görünüyor. Bu yüzden alıcıya gitmek zorunda. Ve bunu alıcıya koyduğunuzda, çeşitli @Column
ek açıklamalar alıcılara da gitmelidir. (Sanırım Hibernate burada alanları ve alıcıları karıştırmak istemiyor.) Ve @Column
bir sınıfta alıcıları koyduğunuzda , muhtemelen bunu yapmak mantıklı.
Saha erişimcilerini destekliyorum. Kod çok daha temiz. Tüm ek açıklamalar bir sınıfın bir bölümüne yerleştirilebilir ve kodun okunması çok daha kolaydır.
Özellik erişimciler ile başka bir sorun buldum: Eğer sınıfta kalıcı özellikleri ile ilişkili olarak açıklanamayan getXYZ yöntemleri varsa, hazırda bekletme bazı özellikleri çok karmaşık hata iletileri ile sonuçlanan denemek için sql oluşturur. İki saat boşa gider. Bu kodu yazmadım; Geçmişte her zaman saha erişimcilerini kullandım ve bu sorunla hiç karşılaşmadım.
Bu uygulamada kullanılan hazırda bekleme sürümleri:
<!-- hibernate -->
<hibernate-core.version>3.3.2.GA</hibernate-core.version>
<hibernate-annotations.version>3.4.0.GA</hibernate-annotations.version>
<hibernate-commons-annotations.version>3.1.0.GA</hibernate-commons-annotations.version>
<hibernate-entitymanager.version>3.4.0.GA</hibernate-entitymanager.version>
Özellikler üzerinden erişim üzerinden alanlar üzerinden erişim seçmelisiniz. Alanlarla gönderilen ve alınan verileri sınırlayabilirsiniz. Via özellikleri ile bir ana bilgisayar olarak daha fazla veri gönderebilir ve G mezhepleri ayarlayabilirsiniz (hangi fabrika toplam özelliklerin çoğunu ayarlar).
Hazırda beklemede accesstype ile ilgili aynı soru vardı ve burada bazı cevaplar buldum .
Tembel başlatma ve alan erişimini burada çözdüm Bire bir hazırda bekletme: tüm nesneyi getirmeden getId ()
Varlık fasulyeleri oluşturduk ve alıcı ek açıklamaları kullandık. Karşılaştığımız sorun şudur: bazı kuruluşlar, ne zaman güncellenebileceklerine ilişkin bazı özellikler için karmaşık kurallara sahiptir. Çözüm, her ayarlayıcıda, gerçek değerin değişip değişmediğini ve değişiyorsa değişikliğe izin verilip verilmeyeceğini belirleyen bir iş mantığına sahip olmaktı. Elbette, Hibernate her zaman özellikleri ayarlayabilir, böylece iki grup ayarlayıcı ile sonuçlandık. Oldukça çirkin.
Önceki yayınları okurken, varlık içerisindeki özelliklere başvurmanın, yüklenmeyen koleksiyonlarla ilgili sorunlara yol açabileceğini de görüyorum.
Sonuç olarak, gelecekte alanlara açıklama eklemek için eğilirdim.
Normalde fasulye POJO, bu yüzden zaten erişim var.
Yani soru "hangisi daha iyi?" Değil, basitçe "alan erişimi ne zaman kullanılır?". Ve cevap "alan için bir ayarlayıcı / alıcıya ihtiyaç duymadığınızda!".
Bunu düşünüyorum ve yöntem aksesuarı seçiyorum
neden?
çünkü alan ve metot aksesuarı aynıdır ancak daha sonra yük alanında mantığa ihtiyacım olursa, alanlara yerleştirilen tüm notları taşıyorum
Saygılarımızla
Grubhart
Her ikisi de :
EJB3 spesifikasyonu, erişilecek öğe türüyle ilgili ek açıklamaları, yani alan erişimini kullanıyorsanız alan erişimini, alan erişim yöntemini beyan etmenizi gerektirir.
https://docs.jboss.org/hibernate/annotations/3.5/reference/en/html_single/#entity-mapping
AccessType.PROPERTY: EJB kalıcılık uygulaması, JavaBean "setter" yöntemleri aracılığıyla sınıfınıza durumu yükler ve JavaBean "getter" yöntemlerini kullanarak sınıfınızdan durum alır. Bu varsayılan ayardır.
AccessType.FIELD: Durum doğrudan sınıfınızın alanlarından yüklenir ve alınır. JavaBean "getters" ve "setters" yazmak zorunda değilsiniz.