Diese Frage hat etwas mit der Frage zur Platzierung von Anmerkungen im Ruhezustand zu tun .
Aber ich möchte wissen, was besser ist ? Zugang über Eigenschaften oder Zugang über Felder? Was sind die Vor- und Nachteile von jedem?
Diese Frage hat etwas mit der Frage zur Platzierung von Anmerkungen im Ruhezustand zu tun .
Aber ich möchte wissen, was besser ist ? Zugang über Eigenschaften oder Zugang über Felder? Was sind die Vor- und Nachteile von jedem?
Antworten:
Ich bevorzuge Accessoren, da ich meinen Accessoren jederzeit eine Geschäftslogik hinzufügen kann. Hier ist ein Beispiel:
@Entity
public class Person {
@Column("nickName")
public String getNickName(){
if(this.name != null) return generateFunnyNick(this.name);
else return "John Doe";
}
}
Wenn Sie andere Bibliotheken in den Mix einfügen (z. B. eine JSON-konvertierende Bibliothek oder BeanMapper oder Dozer oder eine andere Bean-Mapping- / Klon-Bibliothek basierend auf Getter / Setter-Eigenschaften), haben Sie außerdem die Garantie, dass die Bibliothek mit der Persistenz synchronisiert ist Manager (beide verwenden den Getter / Setter).
Es gibt Argumente für beide, aber die meisten von ihnen stammen aus bestimmten Benutzeranforderungen "Was ist, wenn Sie Logik hinzufügen müssen für" oder "xxxx unterbricht die Kapselung". Allerdings hat niemand die Theorie wirklich kommentiert und ein richtig begründetes Argument vorgebracht.
Was macht Hibernate / JPA tatsächlich, wenn es ein Objekt beibehält? Nun, es behält den STATE des Objekts bei. Das bedeutet, dass es so gespeichert wird, dass es leicht reproduziert werden kann.
Was ist Kapselung? Kapselung bedeutet, die Daten (oder den Status) mit einer Schnittstelle zu kapseln, über die die Anwendung / der Client sicher auf die Daten zugreifen kann, um sie konsistent und gültig zu halten.
Stellen Sie sich das wie MS Word vor. MS Word verwaltet ein Modell des Dokuments im Speicher - die Dokumente STATE. Es bietet eine Benutzeroberfläche, über die der Benutzer das Dokument ändern kann - eine Reihe von Schaltflächen, Werkzeugen, Tastaturbefehlen usw. Wenn Sie dieses Dokument jedoch beibehalten (speichern), wird der interne Status gespeichert, nicht der Satz von Tastendrücken und Mausklicks zum Generieren.
Durch das Speichern des internen Status des Objekts wird die Kapselung NICHT unterbrochen. Andernfalls verstehen Sie nicht wirklich, was Kapselung bedeutet und warum sie vorhanden ist. Es ist wirklich wie bei der Objektserialisierung.
Aus diesem Grund ist es in den meisten Fällen angebracht, die FELDER und nicht die ZUGRIFFE beizubehalten. Dies bedeutet, dass ein Objekt genau so aus der Datenbank wiederhergestellt werden kann, wie es gespeichert wurde. Es sollte keine Validierung erforderlich sein, da dies auf dem Original durchgeführt wurde, als es erstellt wurde und bevor es in der Datenbank gespeichert wurde (es sei denn, Gott bewahre, Sie speichern ungültige Daten in der Datenbank !!!!). Ebenso sollte es nicht erforderlich sein, Werte zu berechnen, da diese bereits vor dem Speichern des Objekts berechnet wurden. Das Objekt sollte genauso aussehen wie vor dem Speichern. In der Tat erhöhen Sie durch Hinzufügen zusätzlicher Elemente zu den Gettern / Setzern das Risiko, dass Sie etwas neu erstellen, das keine exakte Kopie des Originals ist.
Natürlich wurde diese Funktionalität aus einem bestimmten Grund hinzugefügt. Es kann einige gültige Anwendungsfälle für das Fortbestehen der Accessoren geben, diese sind jedoch normalerweise selten. Ein Beispiel könnte sein, dass Sie vermeiden möchten, einen berechneten Wert beizubehalten, obwohl Sie möglicherweise die Frage stellen möchten, warum Sie ihn nicht bei Bedarf im Getter des Werts berechnen oder ihn im Getter träge initialisieren. Persönlich kann ich mir keinen guten Anwendungsfall vorstellen, und keine der Antworten hier gibt wirklich eine "Software Engineering" -Antwort.
Ich bevorzuge den Feldzugriff, da ich auf diese Weise nicht gezwungen bin, für jede Eigenschaft Getter / Setter bereitzustellen.
Eine schnelle Umfrage über Google zeigt, dass der Feldzugriff die Mehrheit ist (z . B. http://java.dzone.com/tips/12-feb-jpa-20-why-accesstype ).
Ich glaube, dass der Feldzugriff die von Spring empfohlene Redewendung ist, aber ich kann keinen Hinweis finden, um dies zu belegen.
Es gibt eine verwandte SO-Frage , die versucht hat, die Leistung zu messen, und zu dem Schluss gekommen ist, dass es "keinen Unterschied" gibt.
Hier ist eine Situation, in der Sie Eigenschaften-Accessoren verwenden MÜSSEN. Stellen Sie sich vor, Sie haben eine GENERIC abstrakte Klasse mit viel Implementierungsgüte, die Sie in 8 konkrete Unterklassen erben können:
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
}
Wie genau sollten Sie diese Klasse kommentieren? Die Antwort lautet: SIE KÖNNEN es weder mit Feld- noch mit Eigenschaftszugriff kommentieren, da Sie die Zielentität zu diesem Zeitpunkt nicht angeben können. Sie MÜSSEN die konkreten Implementierungen mit Anmerkungen versehen. Da die persistierten Eigenschaften in dieser Oberklasse deklariert sind, MÜSSEN Sie den Eigenschaftszugriff in den Unterklassen verwenden.
Feldzugriff ist in einer Anwendung mit abstrakten generischen Superklassen keine Option.
abstract T getOneThing()
und abstract void setOneThing(T thing)
und verwenden.
Ich bevorzuge und benutze Property Accessors:
foo.getId()
ohne einen Proxy zu initialisieren (wichtig bei Verwendung von Hibernate, bis HHH-3718 aufgelöst ist).Nachteil:
@Transient
befindet.Das hängt wirklich von einem bestimmten Fall ab - beide Optionen sind aus einem bestimmten Grund verfügbar. IMO läuft es auf drei Fälle hinaus:
Ich würde den Feldzugriff und NICHT Anmerkungen zu den Gettern (Eigenschaftszugriff) dringend empfehlen, wenn Sie in den Setzern mehr tun möchten, als nur den Wert festzulegen (z. B. Verschlüsselung oder Berechnung).
Das Problem beim Eigenschaftszugriff besteht darin, dass die Setter auch beim Laden des Objekts aufgerufen werden. Dies hat für mich viele Monate lang gut funktioniert, bis wir die Verschlüsselung einführen wollten. In unserem Anwendungsfall wollten wir ein Feld im Setter verschlüsseln und im Getter entschlüsseln. Das Problem beim Zugriff auf Eigenschaften bestand nun darin, dass Hibernate beim Laden des Objekts auch den Setter aufrief, um das Feld zu füllen, und somit den verschlüsselten Wert erneut verschlüsselte. In diesem Beitrag wird auch Folgendes erwähnt: Java Hibernate: Unterschiedliches Verhalten der Eigenschaftssatzfunktion, je nachdem, wer es aufruft
Dies hat mir Kopfschmerzen bereitet, bis ich mich an den Unterschied zwischen Feldzugriff und Eigenschaftszugriff erinnerte. Jetzt habe ich alle meine Anmerkungen vom Eigenschaftszugriff zum Feldzugriff verschoben und es funktioniert jetzt einwandfrei.
Ich bevorzuge die Verwendung des Feldzugriffs aus folgenden Gründen:
Der Eigenschaftszugriff kann zu sehr schlimmen Fehlern führen, wenn equals / hashCode implementiert und Felder direkt referenziert werden (im Gegensatz zu ihren Gettern). Dies liegt daran, dass der Proxy nur initialisiert wird, wenn auf die Getter zugegriffen wird, und ein Direktfeldzugriff einfach null zurückgeben würde.
Für den Eigenschaftszugriff müssen Sie alle Dienstprogrammmethoden (z. B. addChild / removeChild) als mit Anmerkungen versehen @Transient
.
Mit dem Feldzugriff können wir das @ Versionsfeld ausblenden, indem wir überhaupt keinen Getter verfügbar machen. Ein Getter kann auch dazu führen, dass ein Setter hinzugefügt wird, und das version
Feld sollte niemals manuell eingestellt werden (was zu sehr unangenehmen Problemen führen kann). Alle Versionsinkrementierungen sollten durch explizites Sperren von OPTIMISTIC_FORCE_INCREMENT oder PESSIMISTIC_FORCE_INCREMENT ausgelöst werden .
version
Felder ist häufig in Situationen nützlich, in denen DTOs anstelle von getrennten Entitäten verwendet werden.
Ich denke, das Annotieren der Eigenschaft ist besser, da das Aktualisieren von Feldern die Kapselung direkt unterbricht, selbst wenn Ihr ORM dies tut.
Hier ist ein großartiges Beispiel dafür, wo es Sie verbrennen wird: Sie möchten wahrscheinlich, dass sich Ihre Anmerkungen für den Validator und die Persistenz im Ruhezustand an derselben Stelle befinden (entweder Felder oder Eigenschaften). Wenn Sie Ihre Validatoren im Ruhezustand testen möchten, die in einem Feld mit Anmerkungen versehen sind, können Sie keinen Mock Ihrer Entität verwenden, um Ihren Komponententest nur auf den Validator zu isolieren. Autsch.
Ich glaube, dass der Zugriff auf Immobilien im Vergleich zum Zugriff auf Felder in Bezug auf die verzögerte Initialisierung geringfügig unterschiedlich ist.
Betrachten Sie die folgenden Zuordnungen für 2 grundlegende Bohnen:
<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>
Und die folgenden Unit-Tests:
@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();
}
Sie werden den subtilen Unterschied in den erforderlichen Auswahlen sehen:
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
Das heißt, für das Anrufen ist fb.getId()
eine Auswahl erforderlich , für das Aufrufen pb.getId()
jedoch nicht.
Lassen Sie mich versuchen, die wichtigsten Gründe für die Wahl des feldbasierten Zugriffs zusammenzufassen. Wenn Sie tiefer eintauchen möchten, lesen Sie bitte diesen Artikel in meinem Blog: Zugriffsstrategien in JPA und Hibernate - Was ist besser, Feld- oder Immobilienzugriff?
Feldbasierter Zugriff ist bei weitem die bessere Option. Hier sind 5 Gründe dafür:
Grund 1: Bessere Lesbarkeit Ihres Codes
Wenn Sie den feldbasierten Zugriff verwenden, versehen Sie Ihre Entitätsattribute mit Ihren Zuordnungsanmerkungen. Wenn Sie die Definition aller Entitätsattribute ganz oben in Ihrer Klasse platzieren, erhalten Sie eine relativ kompakte Ansicht aller Attribute und ihrer Zuordnungen.
Grund 2: Lassen Sie Getter- oder Setter-Methoden weg, die von Ihrer Anwendung nicht aufgerufen werden sollten
Ein weiterer Vorteil des feldbasierten Zugriffs besteht darin, dass Ihr Persistenzanbieter, z. B. Hibernate oder EclipseLink, die Getter- und Setter-Methoden Ihrer Entitätsattribute nicht verwendet. Das bedeutet, dass Sie keine Methode angeben müssen, die von Ihrem Geschäftscode nicht verwendet werden sollte. Dies ist am häufigsten bei Setter-Methoden für generierte Primärschlüsselattribute oder Versionsspalten der Fall . Ihr Persistenzanbieter verwaltet die Werte dieser Attribute, und Sie sollten sie nicht programmgesteuert festlegen.
Grund 3: Flexible Implementierung von Getter- und Setter-Methoden
Da Ihr Persistenzanbieter die Getter- und Setter-Methoden nicht aufruft, sind sie nicht gezwungen, externe Anforderungen zu erfüllen. Sie können diese Methoden beliebig implementieren. Auf diese Weise können Sie geschäftsspezifische Validierungsregeln implementieren, zusätzliche Geschäftslogik auslösen oder das Entitätsattribut in einen anderen Datentyp konvertieren.
Sie können dies beispielsweise verwenden, um eine optionale Zuordnung oder ein Attribut in Java zu verpacken Optional
.
Grund 4: Dienstprogrammmethoden müssen nicht als markiert werden @Transient
Ein weiterer Vorteil der feldbasierten Zugriffsstrategie besteht darin, dass Sie Ihre Dienstprogrammmethoden nicht mit Anmerkungen versehen müssen @Transient
. Diese Anmerkung teilt Ihrem Persistenzanbieter mit, dass eine Methode oder ein Attribut nicht Teil des persistenten Status der Entität ist. Und da beim Zugriff vom Feldtyp der persistente Status durch die Attribute Ihrer Entität definiert wird, ignoriert Ihre JPA-Implementierung alle Methoden Ihrer Entität.
Grund 5: Vermeiden Sie Fehler bei der Arbeit mit Proxys
Hibernate verwendet Proxys für das langsame Abrufen von Zuordnungen, sodass die Initialisierung dieser Zuordnungen gesteuert werden kann. Dieser Ansatz funktioniert in fast allen Situationen einwandfrei. Es führt jedoch zu einer gefährlichen Gefahr, wenn Sie den eigenschaftsbasierten Zugriff verwenden.
Wenn Sie den eigenschaftsbasierten Zugriff verwenden, initialisiert Hibernate die Attribute des Proxy-Objekts, wenn Sie die Getter-Methode aufrufen. Dies ist immer dann der Fall, wenn Sie das Proxy-Objekt in Ihrem Geschäftscode verwenden. Viele Gleichheits- und HashCode-Implementierungen greifen jedoch direkt auf die Attribute zu. Wenn Sie zum ersten Mal auf eines der Proxy-Attribute zugreifen, sind diese Attribute immer noch nicht initialisiert.
Standardmäßig greifen JPA-Anbieter auf die Werte von Entitätsfeldern zu und ordnen diese Felder Datenbankspalten mit den Methoden JavaBean Property Accessor (Getter) und Mutator (Setter) der Entität zu. Daher spielen die Namen und Typen der privaten Felder in einer Entität für JPA keine Rolle. Stattdessen betrachtet JPA nur die Namen und Rückgabetypen der JavaBean-Eigenschaftszugriffsberechtigten. Sie können dies mithilfe der @javax.persistence.Access
Anmerkung ändern, mit der Sie die Zugriffsmethode, die der JPA-Anbieter verwenden soll, explizit angeben können.
@Entity
@Access(AccessType.FIELD)
public class SomeEntity implements Serializable
{
...
}
Die verfügbaren Optionen für die AccessType-Enumeration sind PROPERTY (Standardeinstellung) und FIELD. Mit PROPERTY ruft der Anbieter Feldwerte mithilfe der JavaBean-Eigenschaftsmethoden ab und legt diese fest. Mit FIELD kann der Anbieter Feldwerte mithilfe der Instanzfelder abrufen und festlegen. Als bewährte Methode sollten Sie sich nur an die Standardeinstellungen halten und JavaBean-Eigenschaften verwenden, sofern Sie keinen zwingenden Grund haben, etwas anderes zu tun.
Sie können diese Eigenschaftsanmerkungen entweder in die privaten Felder oder in die öffentlichen Zugriffsmethoden einfügen. Wenn Sie AccessType.PROPERTY
die privaten Felder anstelle der JavaBean-Accessoren verwenden (Standard) und mit Anmerkungen versehen, müssen die Feldnamen mit den JavaBean-Eigenschaftsnamen übereinstimmen. Die Namen müssen jedoch nicht übereinstimmen, wenn Sie die JavaBean-Accessoren mit Anmerkungen versehen. Wenn Sie AccessType.FIELD
die JavaBean-Accessoren anstelle der Felder verwenden und mit Anmerkungen versehen, müssen die Feldnamen ebenfalls mit den JavaBean-Eigenschaftsnamen übereinstimmen. In diesem Fall müssen sie nicht übereinstimmen, wenn Sie die Felder mit Anmerkungen versehen. Es ist am besten, nur konsistent zu sein und die JavaBean-Accessoren für AccessType.PROPERTY
und die Felder für mit
Anmerkungen zu versehen AccessType.FIELD
.
Es ist wichtig, dass Sie niemals JPA-Eigenschaftsanmerkungen und JPA-Feldanmerkungen in derselben Entität mischen. Dies führt zu nicht angegebenem Verhalten und führt sehr wahrscheinlich zu Fehlern.
Das ist eine alte Präsentation, aber Rod schlägt vor, dass Anmerkungen zum Zugriff auf Eigenschaften anämische Domänenmodelle fördern und nicht die "Standard" -Methode für Anmerkungen sein sollten.
Ein weiterer Punkt für den Feldzugriff ist, dass Sie andernfalls auch Setter für Sammlungen verfügbar machen müssen, was für mich eine schlechte Idee ist, da das Ändern der persistenten Sammlungsinstanz in ein Objekt, das nicht von Hibernate verwaltet wird, Ihre Datenkonsistenz definitiv beeinträchtigt.
Daher bevorzuge ich es, Sammlungen als geschützte Felder zu initialisieren, um Implementierungen im Standardkonstruktor zu leeren und nur deren Getter verfügbar zu machen. Dann wird nur verwaltete Operationen wie clear()
, remove()
, removeAll()
usw. sind möglich, die nie Hibernate von Änderungen nicht bewusst machen.
Ich bevorzuge Felder, aber ich bin auf eine Situation gestoßen, die mich zu zwingen scheint, die Anmerkungen auf Getter zu setzen.
Mit der Hibernate JPA-Implementierung @Embedded
scheint es nicht auf Feldern zu funktionieren. Das muss also auf den Punkt gebracht werden. Und sobald Sie das auf den Getter setzen, müssen die verschiedenen @Column
Anmerkungen auch auf den Getter gehen. (Ich denke, Hibernate möchte hier keine Felder und Getter mischen.) Und wenn Sie einmal @Column
Getter in einer Klasse angelegt haben, ist es wahrscheinlich sinnvoll, dies durchgehend zu tun.
Ich bevorzuge Feldzugänge. Der Code ist viel sauberer. Alle Anmerkungen können in einem Abschnitt einer Klasse platziert werden, und der Code ist viel einfacher zu lesen.
Ich habe ein weiteres Problem mit Eigenschaftenzugriffern festgestellt: Wenn Ihre Klasse getXYZ-Methoden enthält, die NICHT als mit persistenten Eigenschaften verknüpft gekennzeichnet sind, generiert der Ruhezustand SQL, um zu versuchen, diese Eigenschaften abzurufen, was zu einigen sehr verwirrenden Fehlermeldungen führt. Zwei Stunden verschwendet. Ich habe diesen Code nicht geschrieben. Ich habe in der Vergangenheit immer Feldzugriffe verwendet und bin nie auf dieses Problem gestoßen.
In dieser App verwendete Versionen für den Ruhezustand:
<!-- 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>
Sie sollten den Zugriff über Felder dem Zugriff über Eigenschaften vorziehen. Mit Feldern können Sie die gesendeten und empfangenen Daten begrenzen. Mit via-Eigenschaften können Sie mehr Daten als Host senden und G-Nennwerte festlegen (die werkseitig die meisten Eigenschaften insgesamt festlegen).
Ich hatte die gleiche Frage bezüglich des Zugangstyps im Ruhezustand und fand hier einige Antworten .
Ich habe die verzögerte Initialisierung und den Feldzugriff hier gelöst. Ruhezustand eins zu eins: getId (), ohne das gesamte Objekt abzurufen
Wir haben Entity Beans erstellt und Getter-Annotationen verwendet. Das Problem, auf das wir gestoßen sind, ist folgendes: Einige Entitäten haben komplexe Regeln für einige Eigenschaften, wann sie aktualisiert werden können. Die Lösung bestand darin, in jedem Setter eine Geschäftslogik zu haben, die bestimmt, ob sich der tatsächliche Wert geändert hat oder nicht, und wenn ja, ob die Änderung zulässig sein sollte. Natürlich kann Hibernate die Eigenschaften immer festlegen, sodass wir zwei Gruppen von Setzern hatten. Ziemlich hässlich.
Beim Lesen früherer Beiträge sehe ich auch, dass das Verweisen auf die Eigenschaften innerhalb der Entität zu Problemen mit nicht geladenen Sammlungen führen kann.
Unterm Strich würde ich mich in Zukunft dazu neigen, die Felder mit Anmerkungen zu versehen.
Normalerweise sind Bohnen POJO, also haben sie sowieso Accessoren.
Die Frage lautet also nicht "Welches ist besser?", Sondern einfach "Wann soll der Feldzugriff verwendet werden?". Und die Antwort lautet "wenn Sie keinen Setter / Getter für das Feld brauchen!".
Ich denke darüber nach und wähle die Methode accesor
Warum?
Da Feld- und Methos-Zugriff identisch sind, aber wenn ich später eine Logik im Ladefeld benötige, speichere ich das Verschieben aller in Feldern platzierten Anmerkungen
Grüße
Grubhart
Beide :
Die EJB3-Spezifikation erfordert, dass Sie Anmerkungen zu dem Elementtyp deklarieren, auf den zugegriffen werden soll, dh die Getter-Methode, wenn Sie den Eigenschaftszugriff verwenden, das Feld, wenn Sie den Feldzugriff verwenden.
https://docs.jboss.org/hibernate/annotations/3.5/reference/en/html_single/#entity-mapping
AccessType.PROPERTY: Die EJB-Persistenzimplementierung lädt den Status über JavaBean-Setter-Methoden in Ihre Klasse und ruft den Status mit JavaBean-Getter-Methoden aus Ihrer Klasse ab. Dies ist die Standardeinstellung.
AccessType.FIELD: Der Status wird direkt aus den Feldern Ihrer Klasse geladen und abgerufen. Sie müssen keine JavaBean "Getter" und "Setter" schreiben.