Ich habe mit JPA (Implementierung Hibernate) für einige Zeit jetzt gearbeitet und jedes Mal, wenn ich Entitäten erstellen müssen, finde ich mich mit Fragen wie AccessType, unveränderliche Eigenschaften, gleich/hashCode, ... kämpfen.
Deshalb habe ich mich entschlossen, die allgemein besten Praktiken für jedes Thema herauszufinden und diese für den persönlichen Gebrauch aufzuschreiben.
Ich hätte jedoch nichts dagegen, wenn sich jemand dazu äußern oder mir sagen würde, wo ich falsch liege.
Entitätsklasse
-
implementieren Serializable
Der Grund: Die Spezifikation besagt, dass Sie dies tun müssen, aber einige JPA-Anbieter setzen dies nicht durch. Hibernate als JPA-Anbieter erzwingt dies nicht, aber es kann irgendwo tief in seinem Magen mit ClassCastException fehlschlagen, wenn Serializable nicht implementiert wurde.
Konstrukteure
-
einen Konstruktor mit allen erforderlichen Feldern der Entität erstellen
Der Grund: Ein Konstruktor sollte die erzeugte Instanz immer in einem intakten Zustand belassen.
-
neben diesem Konstruktor: einen privaten Standardkonstruktor für das Paket haben
Der Grund: Der Standardkonstruktor ist erforderlich, damit Hibernate die Entität initialisieren kann; private ist zulässig, aber die Sichtbarkeit von package private (oder public) ist für die Proxy-Generierung zur Laufzeit und den effizienten Datenabruf ohne Bytecode-Instrumentierung erforderlich.
Felder/Eigenschaften
-
Nutzung des Feldzugangs im Allgemeinen und des Grundstückszugangs bei Bedarf
Grund: Dies ist wahrscheinlich die strittigste Frage, da es keine klaren und überzeugenden Argumente für das eine oder das andere gibt (Eigenschaftszugriff vs. Feldzugriff); der Feldzugriff scheint jedoch der allgemeine Favorit zu sein, da der Code klarer ist, die Kapselung besser ist und keine Setter für unveränderliche Felder erstellt werden müssen.
-
Setter für unveränderliche Felder weglassen (nicht erforderlich für Feld mit Zugriffstyp)
-
Eigenschaften können privat sein
Grund: Ich habe einmal gehört, dass geschützt besser für die (Hibernate) Leistung ist, aber alles, was ich im Internet finden kann, ist: Hibernate kann auf öffentliche, private und geschützte Accessor-Methoden sowie auf öffentliche, private und geschützte Felder direkt zugreifen. Die Wahl liegt bei Ihnen und Sie können sie an das Design Ihrer Anwendung anpassen.
Gleiche/HashCode
- Verwenden Sie niemals eine generierte ID, wenn diese ID nur beim Persistieren der Entität gesetzt wird
- Vorzugsweise: Verwendung unveränderlicher Werte zur Bildung eines eindeutigen Geschäftsschlüssels und Verwendung dieses Schlüssels zur Prüfung der Gleichheit
- Wenn kein eindeutiger Business Key verfügbar ist, verwenden Sie einen nicht-veränderlichen UUID die bei der Initialisierung der Entität erstellt wird; siehe dieser großartige Artikel für weitere Informationen.
- niemals verweisen auf verwandte Entitäten (ManyToOne); wenn diese Entität (wie eine übergeordnete Entität) Teil des Business Key sein muss, dann vergleichen Sie nur die IDs. Der Aufruf von getId() auf einem Proxy wird das Laden der Entität nicht auslösen, solange Sie die Funktion Art des Eigentumszugriffs .
Beispiel Entität
@Entity
@Table(name = "ROOM")
public class Room implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue
@Column(name = "room_id")
private Integer id;
@Column(name = "number")
private String number; //immutable
@Column(name = "capacity")
private Integer capacity;
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "building_id")
private Building building; //immutable
Room() {
// default constructor
}
public Room(Building building, String number) {
// constructor with required field
notNull(building, "Method called with null parameter (application)");
notNull(number, "Method called with null parameter (name)");
this.building = building;
this.number = number;
}
@Override
public boolean equals(final Object otherObj) {
if ((otherObj == null) || !(otherObj instanceof Room)) {
return false;
}
// a room can be uniquely identified by it's number and the building it belongs to; normally I would use a UUID in any case but this is just to illustrate the usage of getId()
final Room other = (Room) otherObj;
return new EqualsBuilder().append(getNumber(), other.getNumber())
.append(getBuilding().getId(), other.getBuilding().getId())
.isEquals();
//this assumes that Building.id is annotated with @Access(value = AccessType.PROPERTY)
}
public Building getBuilding() {
return building;
}
public Integer getId() {
return id;
}
public String getNumber() {
return number;
}
@Override
public int hashCode() {
return new HashCodeBuilder().append(getNumber()).append(getBuilding().getId()).toHashCode();
}
public void setCapacity(Integer capacity) {
this.capacity = capacity;
}
//no setters for number, building nor id
}
Andere Vorschläge zur Ergänzung dieser Liste sind mehr als willkommen...
UPDATE
Seit dem Lesen dieser Artikel Ich habe meine Art der Umsetzung von eq/hC angepasst:
- wenn ein unveränderlicher einfacher Geschäftsschlüssel verfügbar ist: verwenden Sie diesen
- in allen anderen Fällen: eine uuid verwenden