Een overzicht van ID's in Hibernate / JPA

1. Inleiding

Identifiers in Hibernate vertegenwoordigen de primaire sleutel van een entiteit. Dit houdt in dat de waarden uniek zijn, zodat ze een specifieke entiteit kunnen identificeren, dat ze niet nul zijn en dat ze niet zullen worden gewijzigd.

Hibernate biedt een paar verschillende manieren om ID's te definiëren. In dit artikel bespreken we elke methode voor het toewijzen van entiteits-ID's met behulp van de bibliotheek.

2. Eenvoudige identificatiegegevens

De meest eenvoudige manier om een ​​identifier te definiëren is door de @ID kaart annotatie.

Eenvoudige ID's worden in kaart gebracht met @ID kaart naar een enkele eigenschap van een van deze typen: Java primitieve en primitieve wrapper-typen, Tekenreeks, Datum, BigDecimal, BigInteger.

Laten we een snel voorbeeld bekijken van het definiëren van een entiteit met een primaire sleutel van het type lang:

@Entity openbare klas Student {@Id privé lange studentId; // standard constructor, getters, setters}

3. Gegenereerde ID's

Als we willen dat de primaire sleutelwaarde automatisch voor ons wordt gegenereerd, we kunnen de @GeneratedValue annotatie.

Dit kan 4 generatietypen gebruiken: AUTO, IDENTITEIT, VOLGORDE, TABEL.

Als we niet expliciet een waarde specificeren, is het generatietype standaard AUTO.

3.1. AUTO Generatie

Als we het standaard generatietype gebruiken, bepaalt de persistentieprovider waarden op basis van het type van het primaire sleutelattribuut. Dit type kan numeriek zijn of UUID.

Voor numerieke waarden is het genereren gebaseerd op een reeks- of tabelgenerator, while UUID waarden gebruiken de UUIDGenerator.

Laten we een voorbeeld bekijken van het toewijzen van een primaire sleutel van een entiteit met behulp van de automatische generatiestrategie:

@Entity openbare klas Student {@Id @GeneratedValue privé lange studentId; // ...}

In dit geval zijn de primaire sleutelwaarden uniek op databaseniveau.

Een interessante functie die in Hibernate 5 is geïntroduceerd, is de UUIDGenerator. Om dit te gebruiken, hoeven we alleen een ID van het type te declareren UUID met @GeneratedValue annotatie:

@Entity openbare klas Cursus {@Id @GeneratedValue privé UUID courseId; // ...}

Hibernate genereert een ID met de vorm "8dd5f315-9788-4d00-87bb-10eed9eff566".

3.2. IDENTITEIT Generatie

Dit type generatie is afhankelijk van de IdentityGenerator die waarden verwacht die worden gegenereerd door een identiteit kolom in de database, wat betekent dat ze automatisch worden opgehoogd.

Om dit generatietype te gebruiken, hoeven we alleen de strategie parameter:

@Entity openbare klasse Student {@Id @GeneratedValue (strategie = GenerationType.IDENTITY) privé lange studentId; // ...}

Een ding om op te merken is dat IDENTITY-generatie batchupdates uitschakelt.

3.3. VOLGORDE Generatie

Om een ​​op volgorde gebaseerde ID te gebruiken, biedt Hibernate de SequenceStyleGenerator klasse.

Deze generator gebruikt reeksen als ze worden ondersteund door onze database, en schakelt over naar het genereren van tabellen als dat niet het geval is.

Om de naam van de reeks aan te passen, kunnen we de @GenericGenerator annotatie met SequenceStyleGenerator-strategie:

@Entity openbare klasse Gebruiker {@Id @GeneratedValue (generator = "sequence-generator") @GenericGenerator (name = "sequence-generator", strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator", parameters = {@Parameter ( name = "sequence_name", value = "user_sequence"), @Parameter (name = "initial_value", value = "4"), @Parameter (name = "increment_size", value = "1")}) privé lange gebruikers-ID; // ...}

In dit voorbeeld hebben we ook een beginwaarde voor de reeks ingesteld, wat betekent dat het genereren van de primaire sleutel begint bij 4.

VOLGORDE is het generatietype dat wordt aanbevolen door de Hibernate-documentatie.

De gegenereerde waarden zijn uniek per reeks. Als u geen sequentienaam opgeeft, gebruikt Hibernate dezelfde naam hibernate_sequence voor verschillende soorten.

3.4. TABEL Generatie

De Tafelgenerator gebruikt een onderliggende databasetabel die segmenten van identificatiewaarden bevat.

Laten we de tabelnaam aanpassen met de @Tafeltennis annotatie:

@Entity public class Department {@Id @GeneratedValue (strategy = GenerationType.TABLE, generator = "table-generator") @TableGenerator (name = "table-generator", table = "dep_ids", pkColumnName = "seq_id", valueColumnName = "seq_value") privé lange depId; // ...}

In dit voorbeeld kunnen we zien dat andere attributen zoals de pkColumnName en waardeColumnName kan ook worden aangepast.

Het nadeel van deze methode is dat deze niet goed schaalbaar is en de prestaties negatief kan beïnvloeden.

Kortom, deze vier generatietypen zullen resulteren in het genereren van vergelijkbare waarden, maar met verschillende databasemechanismen.

3.5. Aangepaste generator

Als we geen van de kant-en-klare strategieën willen gebruiken, we kunnen onze aangepaste generator definiëren door de IdentifierGenerator koppel.

Laten we een generator maken die ID's bouwt die een Draad prefix en een nummer:

openbare klasse MyGenerator implementeert IdentifierGenerator, configureerbare {private String prefix; @Override public Serializable genereren (SharedSessionContractImplementor-sessie, Object obj) gooit HibernateException {String query = String.format ("select% s van% s", session.getEntityPersister (obj.getClass (). GetName (), obj) .getIdentifierPropertyName ( ), obj.getClass (). getSimpleName ()); Stream-id's = session.createQuery (query) .stream (); Long max = ids.map (o -> o.replace (prefix + "-", "")) .mapToLong (Long :: parseLong) .max () .orElse (0L); retourneer prefix + "-" + (max + 1); } @Override public void configure (Type type, Eigenschappen eigenschappen, ServiceRegistry serviceRegistry) gooit MappingException {prefix = properties.getProperty ("prefix"); }}

In dit voorbeeld we overschrijven de genereren () methode van de IdentifierGenerator koppel en zoek eerst het hoogste nummer uit de bestaande primaire sleutels van het formulier prefix-XX.

Vervolgens voegen we 1 toe aan het maximaal gevonden aantal en voegen we de voorvoegsel eigenschap om de nieuw gegenereerde id-waarde te verkrijgen.

Onze klas implementeert ook de Configureerbaar interface, zodat we de voorvoegsel eigendomswaarde in de configureren () methode.

Laten we vervolgens deze aangepaste generator aan een entiteit toevoegen. Voor deze, we kunnen de @GenericGenerator annotatie met een strategie parameter die de volledige klassenaam van onze generatorklasse bevat:

@Entity public class Product {@Id @GeneratedValue (generator = "prod-generator") @GenericGenerator (name = "prod-generator", parameters = @Parameter (name = "prefix", value = "prod"), strategy = "com.baeldung.hibernate.pojo.generator.MyGenerator") private String prodId; // ...}

Merk ook op dat we de prefix-parameter hebben ingesteld op "prod".

Laten we eens kijken naar een snelle JUnit-test voor een beter begrip van de gegenereerde id-waarden:

@Test public void whenSaveCustomGeneratedId_thenOk () {Product product = nieuw product (); session.save (product); Product product2 = nieuw product (); session.save (product2); assertThat (product2.getProdId ()). isEqualTo ("prod-2"); }

Hier was de eerste waarde die werd gegenereerd met het voorvoegsel "prod", "prod-1", gevolgd door "prod-2".

4. Samengestelde identificatoren

Naast de eenvoudige ID's die we tot nu toe hebben gezien, stelt Hibernate ons ook in staat om samengestelde ID's te definiëren.

Een samengestelde id wordt vertegenwoordigd door een primaire sleutelklasse met een of meer permanente attributen.

De primaire sleutelklasse moet aan verschillende voorwaarden voldoen:

  • het moet worden gedefinieerd met @EmbeddedId of @IdClass annotaties
  • het moet openbaar zijn, serialiseerbaar zijn en een openbare no-arg-constructor hebben
  • het zou moeten implementeren is gelijk aan () en hashCode () methoden

De attributen van de klasse kunnen basic, samengesteld of ManyToOne zijn, waarbij verzamelingen en worden vermeden Een op een attributen.

4.1. @EmbeddedId

Om een ​​ID te definiëren met @EmbeddedId, eerst hebben we een primaire sleutelklasse nodig die is geannoteerd met @Embeddable:

@Embeddable openbare klasse OrderEntryPK implementeert Serializable {privé long orderId; privé lang productId; // standard constructor, getters, setters // equals () en hashCode ()}

Vervolgens kunnen we een ID van het type toevoegen OrderEntryPK naar een entiteit die @ gebruiktEmbeddedId:

@Entity openbare klasse OrderEntry {@EmbeddedId privé OrderEntryPK entryId; // ...}

Laten we eens kijken hoe we dit type samengestelde id kunnen gebruiken om de primaire sleutel voor een entiteit in te stellen:

@Test openbare leegte whenSaveCompositeIdEntity_thenOk () {OrderEntryPK entryPK = nieuwe OrderEntryPK (); entryPK.setOrderId (1L); entryPK.setProductId (30L); OrderEntry entry = nieuwe OrderEntry (); entry.setEntryId (entryPK); session.save (binnenkomst); assertThat (entry.getEntryId (). getOrderId ()). isEqualTo (1L); }

Hier de Orderinvoer object heeft een OrderEntryPK primaire id bestaat uit twee attributen: Order ID en Product-ID.

4.2. @IdClass

De @IdClass annotatie is vergelijkbaar met de @EmbeddedId, behalve dat de attributen worden gedefinieerd in de hoofdentiteitsklasse met @ID kaart voor elke.

De primaire-sleutelklasse ziet er hetzelfde uit als voorheen.

Laten we het Orderinvoer voorbeeld met een @IdClass:

@Entity @IdClass (OrderEntryPK.class) openbare klasse OrderEntry {@Id privé lange orderId; @Id privé lang productId; // ...}

Vervolgens kunnen we de id-waarden rechtstreeks op de Orderinvoer voorwerp:

@Test public void whenSaveIdClassEntity_thenOk () {OrderEntry entry = nieuwe OrderEntry (); entry.setOrderId (1L); entry.setProductId (30L); session.save (binnenkomst); assertThat (entry.getOrderId ()). isEqualTo (1L); }

Merk op dat voor beide typen samengestelde ID's de primaire sleutelklasse ook @ManyToOne attributen.

Hibernate maakt het ook mogelijk om primaire sleutels te definiëren die zijn samengesteld uit @ManyToOne verenigingen gecombineerd met @ID kaart annotatie. In dit geval moet de entiteitsklasse ook voldoen aan de voorwaarden van een primaire-sleutelklasse.

Het nadeel van deze methode is dat er geen scheiding is tussen het entiteitsobject en de identifier.

5. Afgeleide identificatoren

Afgeleide ID's worden verkregen uit de associatie van een entiteit met behulp van de @MapsId annotatie.

Laten we eerst een Gebruikersprofiel entiteit die zijn id ontleent aan een een-op-een associatie met de Gebruiker entiteit:

@Entity openbare klasse UserProfile {@Id privé lang profielId; @OneToOne @MapsId privégebruiker; // ...}

Laten we vervolgens controleren of a Gebruikersprofiel instantie heeft dezelfde id als de bijbehorende Gebruiker voorbeeld:

@Test public void whenSaveDerivedIdEntity_thenOk () {User user = nieuwe gebruiker (); session.save (gebruiker); UserProfile profile = nieuwe UserProfile (); profile.setUser (gebruiker); session.save (profiel); assertThat (profile.getProfileId ()). isEqualTo (user.getUserId ()); }

6. Conclusie

In dit artikel hebben we de verschillende manieren gezien waarop we ID's in Hibernate kunnen definiëren.

De volledige broncode van de voorbeelden is te vinden op GitHub.