1. Einleitung
Wenn das Projekt klein ist, besteht die Versuchung, einen Stil zu wählen und auf alle Entitäten als „Unternehmensstandard“ anzuwenden. Das ähnelt der Situation, wenn man einen Universalschlüssel kauft und beschließt, dass er sowohl die Tür, als auch das Auto und den Kühlschrank öffnen soll. Manchmal geht es sogar auf… aber mit verdächtigem Knirschen. In Hibernate ist der Identifikator genau so ein „Schlüssel“, und verschiedene Entitäten benötigen tatsächlich unterschiedliche Eigenschaften dieses Schlüssels.
Der Grund ist einfach: Entitäten im Commerce Persistence Lab spielen unterschiedliche Rollen. Product und PurchaseOrder leben lange, nehmen an Beziehungen teil, auf ihnen basieren Lese- und Aktualisierungsvorgänge, um sie herum drehen sich viele Szenarien. InventorySnapshot ist dagegen im Wesentlichen ein „Messprotokoll“, bei dem jede Zeile natürlich durch die Kombination (product, date) bestimmt wird. ProductCategoryAssignment ist eine Link-Entität mit eigenem Lebenszyklus und zusätzlichen Feldern. Wenn man versucht, sie alle in eine „ID-Schablone“ zu zwängen, vereinfacht man sich entweder das Leben heute oder erschwert es morgen (und leider meistens das Zweite).
Wir haben die Wahl der ID bereits in mehrere Fragen aufgeteilt: wo ein technischer PK benötigt wird, wo eine Business-ID daneben lebt und wo die Identität der Zeile selbst zusammengesetzt ist. Nun müssen wir diese Verzweigungen in Entwurfsentscheidungen für das Commerce Persistence Lab umsetzen.
Kurze Checkliste zur Auswahl
Um die Wahl der ID nicht zur Annotationsroulette zu machen, reicht es, eine kurze Reihenfolge von Fragen zu beachten:
- Wird die Zeile tatsächlich durch eine Kombination von Feldern identifiziert oder ist ein Surrogate-Key ehrlicher?
- Wenn ein Surrogate-Key benötigt wird, ist der Standard für das Projekt ein technischer Long PK mit SEQUENCE;
- Daneben prüfen wir, ob eine stabile Business-ID wie sku, email oder orderNumber benötigt wird;
- Nur bei einem echten Grund weichen wir auf UUID oder assigned id aus.
Das reicht aus, um fast alle Entitäten des Projekts ohne eine zweite oder dritte „universelle“ Strategie zuzuordnen.
2. Projektstandard: Long + SEQUENCE als „gesunde Basis“
Wenn man die Kernentitäten des Projekts durch diese Checkliste laufen lässt, zeigt sich schnell der Standard: ein technischer Surrogate-Key vom Typ Long, generiert über SEQUENCE. Für die Domäne des Katalogs, der Bestellungen und der Bestände ist dies die ruhigste Variante: Fremdschlüssel bleiben kurz, der Insert-Fluss ist vorhersagbar und SQL ist leichter zu lesen und zu diagnostizieren.
Das ist kein Blutschwur. Es bedeutet lediglich: Wenn eine Entität keinen besonderen Grund hat, anders zu leben, erfinden wir keine Exotik um der Exotik willen.
Mini-Codebeispiel (Idee, nicht der „einzig wahre Stil“):
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
@Entity // Macht die Klasse zu einer verwalteten JPA-Entität
public class Product {
@Id // Primärschlüssel (technische Identität der Zeile)
@GeneratedValue(strategy = GenerationType.SEQUENCE) // Generierung der ID über SEQUENCE in der Datenbank
private Long id; // Surrogate Key: kurz und praktisch für FK/Joins
}
Hier wird GenerationType.SEQUENCE grob gezeigt. Im Projekt legen wir die Sequenz nach wie vor explizit über @SequenceGenerator fest und erstellen die Sequenz selbst per Flyway-Migration. Jetzt ist das Urteil über die Rolle der Entität wichtig, nicht das vollständige DDL.
Und ja, Lücken in den Werten für die Sequenz sind normal: Ihre Aufgabe ist es, eindeutige Zahlen zu liefern, nicht eine schöne buchhalterische Nummerierung ohne eine einzige Lücke.
4. Kernentitäten: technische ID + @NaturalId
Für die Kernentitäten basiert fast alles auf diesem Standard: Der PK bleibt technisch, und die Business-ID lebt daneben. Das ist keine Doppelung, sondern eine Rollentrennung: Der PK wird für Hibernate, FK und Joins benötigt; sku, email und orderNumber werden für die Domäne, Logs und Suche benötigt.
Product: Long PK + sku als natural id
Product ist das Herz des Katalogs und der „Held“ des Laborkurses. Wir suchen oft ein Produkt nach sku, wir diskutieren die Eindeutigkeit von sku, wir möchten sku nach einem Soft-Delete nicht wiederverwenden (das kommt später, aber die Idee ist wichtig). Gleichzeitig ist es bequemer, technische Beziehungen (FK) und Joins auf Datenbankebene über die Long ID zu halten. Daher ist die Kombination „Surrogate PK + Natural ID“ hier fast lehrbuchartig, aber im guten Sinne.
import jakarta.persistence.*;
import org.hibernate.annotations.NaturalId;
@Entity // Katalog-Entität
@Table(name = "product") // Explizite Festlegung des Tabellennamens (praktisch für Migrationen/SQL-Logs)
public class Product {
@Id // Technischer PK
@GeneratedValue(strategy = GenerationType.SEQUENCE) // Standardstrategie des Kurses
private Long id;
@NaturalId // Geschäftsidentifikator für Suche und Lesbarkeit der Domäne
@Column(nullable = false, unique = true) // Eindeutigkeit und NOT NULL auf Datenbankebene halten
private String sku;
}
Beachten Sie, wir machen sku nicht zum Primärschlüssel und legen auch nicht alle FK darauf an. Wir sagen einfach: „Das ist ein wichtiger Geschäftsidentifikator, über den man bequem laden und suchen kann.“ Auf Repository-Ebene liest sich das wie normale Domänensprache:
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
public interface ProductRepository extends JpaRepository<Product, Long> {
// Suche nach Geschäftsschlüssel (natural id), nicht nach technischem PK
Optional<Product> findBySku(String sku);
}
Und im Service (mit dem bereits bekannten Muster „find + mutate“ aus den frühen Tagen) wird es noch angenehmer:
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service // Serviceschicht: Hier leben die Geschäftsoperationen
public class ProductService {
// productRepository wird normalerweise über Konstruktor/DI injiziert (zur Kürze des Beispiels weggelassen)
@Transactional // Wichtig: Änderungen an Entitäten müssen in einer Transaktion erfolgen
public void renameProduct(String sku, String newName) {
// Zuerst finden wir über den Geschäftsschlüssel – so liest sich der Code „menschenlesbar“
Product p = productRepository.findBySku(sku).orElseThrow();
// Dann ändern wir einfach den Zustand – Hibernate führt beim Flush/Commit selbst ein Dirty Checking durch
p.setName(newName);
}
}
Letztendlich liest jemand den Code und versteht, dass wir ein Produkt mit einem bestimmten SKU aktualisieren, nicht „eine Entität mit id=12345, die wir irgendwo im Dunkeln gefunden haben“.
Customer: Long PK + email als natural id
Customer ist eine Entität, bei der sich die Natural ID normalerweise von selbst anbietet: E-Mail. Aber hier gibt es eine Feinheit: Das Geschäft kann erlauben, die E-Mail zu ändern. Dann wird die Natural ID mutable. Technisch verkraftet Hibernate das, aber das Modell wird teurer: Innerhalb einer Sitzung muss das ORM die Zuordnung natural id ↔ primary key korrekt aktualisieren. Und im Kopf des Entwicklers taucht eine neue Verantwortung auf: „Mach die E-Mail nicht zum Schlüssel der Welt, wenn sie sich ändern kann.“
Im Rahmen des Laborprojekts kann man zwei Wege gehen. Entweder die E-Mail als stabilen Identifikator festlegen (und ihre Änderung auf Domänenebene verbieten) oder die Änderung erlauben, aber als separates Geschäftsszenario mit bewussten Kosten behandeln. Der Einfachheit halber wählt der Kurs meist die erste Option.
import jakarta.persistence.*;
import org.hibernate.annotations.NaturalId;
@Entity // Benutzer/Kunde des Systems
@Table(name = "customer")
public class Customer {
@Id // Technischer PK, an dem die FK hängen
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
@NaturalId // Geschäftsschlüssel: bequemes Suchen des Kunden per E-Mail
@Column(nullable = false, unique = true) // Duplikate schützen wir durch Constraint in der DB
private String email;
}
Wenn Sie dennoch die Mutabilität zeigen möchten, sieht das so aus:
@NaturalId(mutable = true) // Erlaubt das Ändern des Geschäftsschlüssels (Kosten: schwieriger, die Korrektheit zu wahren)
@Column(nullable = false, unique = true)
private String email;
Aber wichtig ist, dass es eine bewusste Entscheidung ist und nicht „weil es geht“.
PurchaseOrder: Long PK + orderNumber als natural id
Eine Bestellung ist ein klassisches Beispiel einer Entität, bei der das Geschäft nicht in der id, sondern in der orderNumber lebt. Menschen kommunizieren mit der Bestellnummer, suchen die Bestellung nach Nummer, schreiben sie in Mails und an den Support. Aber FK auf orderNumber werden Sie später verfluchen, weil er länger, schwerfälliger ist und Sie irgendwann das Format der Nummer ändern möchten. Daher funktioniert hier wieder das Schema perfekt: PK für Beziehungen und Technik, natural id für die Geschäftskommunikation.
import jakarta.persistence.*;
import org.hibernate.annotations.NaturalId;
@Entity // Bestellung als Kernentität der Domäne
@Table(name = "purchase_order")
public class PurchaseOrder {
@Id // Technischer PK: kurze FK und schnelle Joins
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
@NaturalId // Geschäftliche Bestellnummer: Menschen und Integrationen kommunizieren damit
@Column(nullable = false, unique = true)
private String orderNumber;
}
Und wieder ein Repository, das sich menschenlesbar liest:
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
public interface PurchaseOrderRepository extends JpaRepository<PurchaseOrder, Long> {
// Suche nach Bestellung über Geschäftsnummer (natural id)
Optional<PurchaseOrder> findByOrderNumber(String orderNumber);
}
Category: Long PK + code als natural id
Kategorien sind ein Nachschlagewerk. In Nachschlagewerken gibt es oft einen kurzen Code (ELECTRONICS, BOOKS, HOME) und einen menschenlesbaren Namen. Der Code ist normalerweise die Natural ID: er ist stabil, eindeutig und für die Suche geeignet. FK legen wir wieder auf Long, weil das einfacher und kostengünstiger für die DB ist.
import jakarta.persistence.*;
import org.hibernate.annotations.NaturalId;
@Entity // Kategorienachschlagewerk
@Table(name = "category")
public class Category {
@Id // Technischer PK (FK darauf sind günstiger als auf den String-Code)
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
@NaturalId // Stabiles Code des Nachschlagewerks
@Column(nullable = false, unique = true)
private String code;
}
Hier gewinnen wir zusätzlich, dass code sich bequem in Tests und Seed-Daten verwenden lässt. Wenn ein Test „Kategorie ELECTRONICS“ liest, ist das besser als „Kategorie 7“.
5. Untergeordnete Entitäten von Aggregaten: Surrogate Key
An diesem Punkt denken viele: „Okay, für die Wurzelentitäten gibt es Long-IDs. Aber die „untergeordneten“ haben ja einen Elternteil, also könnte man ihren Identifikator zusammengesetzt machen oder ihnen gar keine separate ID geben.“ Theoretisch – möglich. In der Praxis – das verkompliziert das Leben meist mehr, als es spart.
OrderItem, CustomerAddress, InventoryItem und andere Entitäten, die in der Nähe von Aggregaten leben, existieren dennoch als Zeilen in Tabellen. Sie brauchen einen verständlichen Primärschlüssel, damit die Tabelle normal funktioniert, damit Referenzen arbeiten und Hibernate die Entitätszustände vorhersagbar verwalten kann. Ja, auf OrderItem greifen Sie selten „per ID“ aus dem Geschäftscode zu. Aber ORM und Datenbank greifen zu – ständig.
Daher wählt man im Projekt normalerweise diesen Stil: Die Child-Entität erhält ihren eigenen technischen Surrogate Key (Long + SEQUENCE), und geschäftliche Eindeutigkeiten werden durch separate Constraints und Regeln auf Serviceebene ausgedrückt. Das harmoniert gut mit Kaskaden, Orphan Removal und Helper-Methoden (die wir bereits am Tag über die besitzende Seite und den Lebenszyklus von Beziehungen besprochen haben).
Mini-Skelett für OrderItem:
import jakarta.persistence.*;
@Entity // Bestellposition – eine separate Zeile mit eigenem Lebenszyklus
@Table(name = "order_item")
public class OrderItem {
@Id // Technischer PK der Position
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
@ManyToOne(optional = false) // Jede Position muss zu einer Bestellung gehören
private PurchaseOrder order;
}
Das Gleiche für die Kundenadresse:
import jakarta.persistence.*;
@Entity // Adresse – eine separate Entität (normalerweise können mehrere vorhanden sein)
@Table(name = "customer_address")
public class CustomerAddress {
@Id // Technischer PK der Adresse
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
@ManyToOne(optional = false) // Adresse existiert nicht „für sich allein“, sie ist an den Kunden gebunden
private Customer customer;
}
Und für die Bestände (wichtig: InventoryItem hat einen eigenen Lebenszyklus, konkurrierende Updates und später eine Version für Optimistic/Pessimistic Locking; es ist genau die Entität, der ein Surrogate PK normalerweise nicht im Weg steht, sondern hilft):
import jakarta.persistence.*;
@Entity // Lagerbestand (Entität mit eigenem Zustand und Konkurrenz)
@Table(name = "inventory_item")
public class InventoryItem {
@Id // Technischer PK der Bestandszeile
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
@ManyToOne(optional = false) // Bestand bezieht sich immer auf ein bestimmtes Produkt
private Product product;
}
Ja, für InventoryItem haben wir die Invariante „ein Eintrag pro Produkt“. Das ist kein Grund, product_id zum Primärschlüssel zu machen. Das ist ein Grund, einen Unique Constraint auf product_id zu setzen. Das heißt, der PK ist für die technische Identität der Zeile verantwortlich, und die Eindeutigkeit der Geschäftsregel wird durch einen Constraint ausgedrückt. Diese Trennung macht das Modell stabiler.
6. Link-Entität ProductCategoryAssignment: ohne Composite PK
ProductCategoryAssignment ist genau der Fall, bei dem ein Anfänger besonders oft versucht, es „schön zu machen“: „Es ist doch eine Join-Tabelle, machen wir den PK = (product_id, category_id)“. Und ja, das kann funktionieren. Aber in unserem Projekt ist die Link-Entität nicht einfach eine Verknüpfungstabelle, sondern eine Entität mit zusätzlichen Feldern (sortOrder, assignedAt) und eigenem Lebenszyklus. Und hier gewinnt der Surrogate Key wieder an Einfachheit.
Stellen Sie sich vor, Sie möchten irgendwann auf eine Zuordnung verweisen (z.B. Änderungen protokollieren, Soft Delete auf der Zuordnung durchführen, die Historie audieren). Mit einem Composite PK schleppen Sie überall ein Paar Felder mit sich. Im Repository nimmt findById nicht Long, sondern ein separates Key-Objekt. Im Service konstruieren Sie ständig IDs. In Tests wird daraus ein „Key-Konstruktor um des Key-Konstruktors willen“. Und das alles nur, um eine Spalte id zu sparen … die in PostgreSQL nahezu nichts kostet im Vergleich zum Preis der kognitiven Komplexität.
Daher ist die vernünftige Lösung für das Projekt: Surrogate Key für ProductCategoryAssignment + Eindeutigkeit des Paares (product_id, category_id) durch Constraint.
import jakarta.persistence.*;
@Entity // Link-Entität: nicht nur eine Join-Tabelle, sondern eine Entität mit Verhalten/Feldern
@Table(
name = "product_category_assignment",
uniqueConstraints = @UniqueConstraint(
name = "uk_assignment_product_category",
columnNames = {"product_id", "category_id"} // Invariante: ein Paar Produkt-Kategorie
)
)
public class ProductCategoryAssignment {
@Id // Technischer PK für einfache API und Referenzen auf die Zuordnung
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
@ManyToOne(optional = false) // Beziehung zeigt zwingend auf ein Produkt
private Product product;
@ManyToOne(optional = false) // ...und auf eine Kategorie
private Category category;
}
Beachten Sie: Diese Lösung hält die Invariante ehrlich in der Datenbank (eindeutiges Paar), verwandelt aber Ihren Java-Code nicht in eine ständige Arbeit mit zusammengesetzten Schlüsseln, wo diese keinen Sinn ergeben. Das ist besonders wichtig in einem Lehrprojekt, wo unser Ziel ist, das ORM-Verhalten zu beobachten, nicht unter einer sperrigen API zu leiden.
7. InventorySnapshot: Composite Key entsprechend dem Datensinn
Nun die Ausnahme, die man nicht auf das gesamte Projekt ausdehnen sollte. Für InventorySnapshot ist die Wahl bereits festgelegt: @EmbeddedId mit dem Schlüssel (productId, snapshotDate). Hier ist der Composite Key nicht dekorativ – er stimmt wörtlich mit der Bedeutung der Zeile überein: eine Momentaufnahme von welchem Produkt und zu welchem Datum.
Wenn wir einen Surrogate-Key id hinzugefügt hätten, müssten wir trotzdem separat die Eindeutigkeit von (product_id, snapshot_date) schützen, sonst könnte man zwei Momentaufnahmen desselben Produkts für dasselbe Datum aufzeichnen. Das heißt, wir hätten sowohl einen Surrogate-Key als auch einen Constraint und die Notwendigkeit, sich daran zu erinnern, dass der eigentliche Schlüssel nicht der PK ist. Im Snapshot-Modell wirkt das gekünstelt.
Mini-Skelett des Identifikators:
import jakarta.persistence.Embeddable;
import java.io.Serializable;
import java.time.LocalDate;
@Embeddable // Einbettbarer Typ, der als zusammengesetzter Schlüssel verwendet wird
public class InventorySnapshotId implements Serializable {
private Long productId; // Schlüsselteil: welches Produkt
private LocalDate snapshotDate; // Schlüsselteil: für welches Datum die Momentaufnahme
// Hier müssen korrekte equals()/hashCode() vorhanden sein, da dieses Objekt an der Identität der Zeile beteiligt ist.
}
Und die Entität:
import jakarta.persistence.*;
@Entity // Bestands-Snapshot zum Datum
@Table(name = "inventory_snapshot")
public class InventorySnapshot {
@EmbeddedId // Composite PK: (productId, snapshotDate)
private InventorySnapshotId id;
private Integer quantity; // Snapshot-Wert (z.B. Menge auf Lager)
}
Die Variante mit @IdClass zu lesen ist nützlich, aber wir bauen die Baseline des Projekts für InventorySnapshot nicht darauf.
Die praktische Konsequenz dieser Entscheidung werden Sie sofort auf Repository-Ebene spüren: findById erfordert den vollständigen Schlüssel, nicht „eine einzelne Zahl“. Das ist der Preis für einen Composite Key, und in unserem Projekt ist er genau dadurch gerechtfertigt, dass die Entität snapshot-orientiert ist und normalerweise in Bulk-ähnlichen Szenarien verwendet wird, in denen Sie ohnehin mit (product, date) arbeiten.
8. UUID und assigned id: wann sie angebracht sind
UUID und assigned id sind nicht verboten – sie haben nur keine Standardrolle in der aktuellen Domäne. Die Stärke von UUID ist, dass der Wert auf der Java-Seite vor dem Einfügen erscheint; assigned id ist gut, wenn die Außenwelt bereits einen stabilen Schlüssel mitbringt und ein zweiter „Ausweis“ sinnlos wäre.
Das Problem ist, dass diese Stärken für Katalog, Bestellungen und Bestände kaum einen Gewinn bringen, der den Preis rechtfertigt. UUID macht Schlüssel und Indizes schwerer ohne merklichen Nutzen für unsere Baseline, und assigned id erfordert eine externe stabile Quelle, die es im Projekt nicht gibt. Daher behalten wir sie als bewusste Ausnahmen: UUID – wenn wirklich eine ID vor dem Insert benötigt wird; assigned id – wenn die Identität bereits von außen kommt und genau diese die Quelle der Wahrheit bleibt.
Wie dieses Schema bei einer neuen Entität funktioniert
Wenn im Projekt eine neue Entität auftaucht, beginnen wir nicht mit der Annotation @GeneratedValue, sondern mit der Rolle der Zeile. Wenn die Zeile als Kombination von Feldern lebt – ist sie ein Kandidat für einen Composite Key. Wenn nicht, starten wir mit einem Surrogate PK, entscheiden separat die Business-ID und fragen erst dann, ob es einen echten Grund gibt, das Modell auf UUID oder assigned id umzustellen.
Diese Reihenfolge macht das Verhalten des Projekts vorhersagbar: Man versteht im Voraus, wo Hibernate sich auf den einfachen technischen PK stützen wird, wo der Code in der Sprache von sku/orderNumber sprechen wird und wo der Composite Key wirklich die Bedeutung der Daten widerspiegelt, nicht die Liebe zu komplexen Schemata.
9. Typische Fehler bei der Auswahl von Identifikatoren
Fehler Nr. 1: Eine „Lieblingsstrategie“ wählen und auf alle Entitäten anwenden.
Eine solche Entscheidung erscheint als Vereinfachung, bis Sie auf die erste Entität stoßen, die nicht in das Schema passt. Am Ende machen Sie entweder seltsame Kompromisse („na gut, der Snapshot bekommt auch Long, und das Datum ist einfach ein Feld…“) oder Sie zerbrechen die Hälfte des Projekts wegen einer Ausnahme. Es ist viel gesünder, einen Standard (Long + SEQUENCE) und bewusste Ausnahmen (@EmbeddedId für Snapshot, assigned id bei externer Quelle) zu haben.
Fehler Nr. 2: Die Natural ID zum Primärschlüssel und in alle Fremdschlüssel machen, „weil sie ja geschäftlich ist“.
Die Natural ID wird für die Geschäftssuche und Lesbarkeit benötigt. Aber FK auf sku oder email machen das Schema schwerer, Joins breiter und die Evolution des Modells schmerzhafter. Selbst wenn ein Feld eindeutig ist, muss es nicht die Stütze aller Beziehungen sein. In unserem Projekt trennen wir bewusst: technische Beziehungen leben auf dem Surrogate PK, Geschäftsidentifikation daneben.
Fehler Nr. 3: Automatisch einen Composite Key in einer Link-Entität machen, ohne Analyse des Lebenszyklus.
Der zusammengesetzte Schlüssel in ProductCategoryAssignment wirkt „logisch“, bis Sie sortOrder, assignedAt, Soft Delete, Audit, separate Änderungsoperationen hinzufügen. Ab diesem Moment ist die Link-Entität nicht mehr nur eine „Verklebung“, sondern eine vollwertige Entität. Surrogate Key + Unique Constraint geben dieselbe Invariante, lassen aber das API und das ORM-Verhalten einfacher und vorhersagbarer.
Fehler Nr. 4: Die generated id als Grundlage für equals()/hashCode() ohne Berücksichtigung des Generierungszeitpunkts verwenden.
Das ist ein klassischer Weg, ein Set zu brechen, „verschwindende Elemente“ in Sammlungen und seltsames Verhalten von Orphan Removal zu erhalten. Wir haben bereits besprochen, dass die generated id bis zum persist/flush null sein kann. Wenn Sie einen hashCode auf ein Feld bauen, das sich mitten im Leben des Objekts ändert, verhalten sich Sammlungen wie eine Katze: „eben war sie noch da, dann nicht mehr“. Wenn die Entität einen stabilen Business-Key hat, ist dieser oft sicherer für die Gleichheit, aber nur, wenn er wirklich stabil ist.
Fehler Nr. 5: UUID wählen, „weil es modern ist“, ohne technischen Grund.
UUID ist nützlich, wenn Sie wirklich eine ID im Voraus benötigen, wenn Sie nicht von einer Sequenz abhängig sein wollen, wenn es eine verteilte Generierung gibt und andere solche Faktoren. Aber UUID macht überall die Indizes schwerer, SQL weniger lesbar und stört manchmal sogar die Analyse von Problemen in Logs. In unserem Projekt ist UUID kein Verbot, sondern eine bewusste Ausnahme, der ein Grund vorausgehen muss, nicht eine Stimmung.
GO TO FULL VERSION