Hibernate ein bis viele Anmerkungs-Lernprogramme

Hibernate One to Many Annotation Tutorial

1. Einführung

Dieses kurze Tutorial zum Ruhezustand führt uns durch ein Beispiel für die Zuordnung vonone-to-manymithilfe von JPA-Annotationen - eine Alternative zu XML.

Wir werden auch erfahren, was bidirektionale Beziehungen sind, wie sie zu Inkonsistenzen führen können und wie die Idee desownership -Scans hilft.

Weitere Lektüre:

Spring Boot mit Ruhezustand

Eine schnelle und praktische Einführung in die Integration von Spring Boot und Hibernate / JPA.

Read more

Eine Übersicht der Bezeichner im Ruhezustand

Erfahren Sie, wie Sie Entitäts-IDs mit Hibernate zuordnen.

Read more

2. Beschreibung

Einfach ausgedrückt,one-to-many mapping means that one row in a table is mapped to multiple rows in another table.

Schauen wir uns das folgende Entitätsbeziehungsdiagramm an, um die Zuordnung vonone-to-manyzu sehen:

image

In diesem Beispiel implementieren wir ein Warenkorbsystem, in dem wir für jeden Warenkorb eine Tabelle und für jeden Artikel eine andere Tabelle haben. One cart can have many items, so here we have a one-to-many mapping.

Auf Datenbankebene funktioniert dies so, dass wircart_id als Primärschlüssel in der Tabellecart undcart_id als Fremdschlüssel initems haben.

Und die Art und Weise, wie wir es im Code machen, ist mit@OneToMany.

Ordnen wir die KlasseCartdem ObjektItemso zu, dass sie die Beziehung in der Datenbank widerspiegelt:

public class Cart {

    //...

    @OneToMany(mappedBy="cart")
    private Set items;

    //...
}

Wir können auch einen Verweis aufCart inItems mit@ManyToOne hinzufügen, wodurch dies zu einer Beziehung vonbidirectionalwird. Bidirectional bedeutet, dasswe are able to access items from carts, and also carts from items.

Mit der EigenschaftmappedByteilen wir Hibernate mit, welche Variable wir verwenden, um die übergeordnete Klasse in unserer untergeordneten Klasse darzustellen.

Die folgenden Technologien und Bibliotheken werden verwendet, um eine Beispielanwendung für den Ruhezustand zu entwickeln, die die Zuordnung vonone-to-manyimplementiert:

  • JDK 1,8 oder höher

  • Hibernate 5

  • Maven 3 oder höher

  • H2-Datenbank

3. Konfiguration

3.1. Datenbank-Setup

Unten finden Sie unser Datenbankskript für die TabellenCart undItems. Wir verwenden die Fremdschlüsseleinschränkung für die Zuordnung vonone-to-many:

CREATE TABLE `Cart` (
  `cart_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  PRIMARY KEY (`cart_id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

CREATE TABLE `Items` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `cart_id` int(11) unsigned NOT NULL,
  PRIMARY KEY (`id`),
  KEY `cart_id` (`cart_id`),
  CONSTRAINT `items_ibfk_1` FOREIGN KEY (`cart_id`) REFERENCES `Cart` (`cart_id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;

Das Datenbank-Setup ist fertig. Fahren wir mit der Erstellung des Hibernate-Beispielprojekts fort.

3.2. Maven-Abhängigkeiten

Anschließend fügen wir die Abhängigkeiten des Hibernate- und H2-Treibers zu unsererpom.xml-Datei hinzu. Die Hibernate-Abhängigkeit verwendet die JBoss-Protokollierung und wird automatisch als transitiven Abhängigkeiten hinzugefügt:

  • Ruhezustand Version 5.2.7.Final

  • H2-Treiberversion 1.4.197

Bitte besuchen Sie das zentrale Maven-Repository, um die neuesten Versionen der Abhängigkeiten vonHibernate undH2 zu erhalten.

3.3. Ruhezustand

Hier ist die Konfiguration von Hibernate:


    
        org.h2.Driver
        
        
          jdbc:h2:mem:spring_hibernate_one_to_many
        sa
        org.hibernate.dialect.H2Dialect
        thread
        true
    

3.4. HibernateAnnotationUtil Klasse

Bei der KlasseHibernateAnnotationUtilmüssen wir nur auf die neue Konfigurationsdatei für den Ruhezustand verweisen:

private static SessionFactory sessionFactory;

private SessionFactory buildSessionFactory() {

    ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().
      configure("hibernate-annotation.cfg.xml").build();
    Metadata metadata = new MetadataSources(serviceRegistry).getMetadataBuilder().build();
    SessionFactory sessionFactory = metadata.getSessionFactoryBuilder().build();

    return sessionFactory;
}

public SessionFactory getSessionFactory() {
    if(sessionFactory == null) sessionFactory = buildSessionFactory();
    return sessionFactory;
}

4. Die Models

Die Mapping-bezogenen Konfigurationen werden mit JPA-Annotationen in den Modellklassen durchgeführt:

@Entity
@Table(name="CART")
public class Cart {

    //...

    @OneToMany(mappedBy="cart")
    private Set items;

    // getters and setters
}

Bitte beachten Sie, dass die Annotation@OneToManyverwendet wird, um die Eigenschaft in der KlasseItemszu definieren, die zum Zuordnen der VariablenmappedByverwendet wird. Aus diesem Grund haben wir eine Eigenschaft mit dem Namen "cart" in der Klasse "Items:

@Entity
@Table(name="ITEMS")
public class Items {

    //...
    @ManyToOne
    @JoinColumn(name="cart_id", nullable=false)
    private Cart cart;

    public Items() {}

    // getters and setters
}

Es ist wichtig zu beachten, dass die Annotation@ManyToOneder KlassenvariablenCartzugeordnet ist. Die Annotation von@JoinColumnverweist auf die zugeordnete Spalte.

5. In Aktion

Im Testprogramm erstellen wir eine Klasse mit einermain () -Methode, um die Hibernate-Sitzung abzurufen und die Modellobjekte in der Datenbank zu speichern, die dieone-to-many-Zuordnung implementiert:

sessionFactory = HibernateAnnotationUtil.getSessionFactory();
session = sessionFactory.getCurrentSession();
System.out.println("Session created");

tx = session.beginTransaction();

session.save(cart);
session.save(item1);
session.save(item2);

tx.commit();
System.out.println("Cart ID=" + cart.getId());
System.out.println("item1 ID=" + item1.getId()
  + ", Foreign Key Cart ID=" + item.getCart().getId());
System.out.println("item2 ID=" + item2.getId()
+ ", Foreign Key Cart ID=" + item.getCart().getId());

Dies ist die Ausgabe unseres Testprogramms:

Session created
Hibernate: insert into CART values ()
Hibernate: insert into ITEMS (cart_id)
  values (?)
Hibernate: insert into ITEMS (cart_id)
  values (?)
Cart ID=7
item1 ID=11, Foreign Key Cart ID=7
item2 ID=12, Foreign Key Cart ID=7
Closing SessionFactory

6. Die Annotation@ManyToOne

Wie wir in Abschnitt 2 gesehen haben, können wir die Beziehung vonmany-to-onemithilfe der Annotation von@ManyToOneangeben. Die Zuordnung vonmany-to-onebedeutet, dass viele Instanzen dieser Entität einer Instanz einer anderen Entität zugeordnet werden -many items in one cart.

The @ManyToOne annotation lets us create bidirectional relationships, too. Wir werden dies in den nächsten Unterabschnitten ausführlich behandeln.

6.1. Inkonsistenzen und Eigentum

Wenn nunCartaufItems verweist,Itemsjedoch nicht aufCart,our relationship would be unidirectional verweist. The objects would also have a natural consistency.

In unserem Fall ist die Beziehung jedoch bidirektional,bringing in the possibility of inconsistency.

Stellen wir uns eine Situation vor, in der ein Entwickleritem1 zucart unditem2 zucart2 hinzufügen möchte, aber einen Fehler macht, sodass die Verweise zwischencart2 unditem2 werden inkonsistent:

Cart cart1 = new Cart();
Cart cart2 = new Cart();

Items item1 = new Items(cart1);
Items item2 = new Items(cart2);
Set itemsSet = new HashSet();
itemsSet.add(item1);
itemsSet.add(item2);
cart1.setItems(itemsSet); // wrong!

Wie oben gezeigt, verweisenitem2 aufcart2 whereascart2 nicht aufitem2 — und das ist schlecht.

How should Hibernate save item2 to the database? Wirditem2 Fremdschlüsselreferenzcart1 odercart2?

Wir lösen diese Mehrdeutigkeit mit der Idee einesowning side der Beziehung - Referenzen, die zur Besitzerseite gehören, haben Vorrang und werden in der Datenbank gespeichert.

6.2. items als Besitzerseite

Wie inJPA specification unter Abschnitt 2.9 angegeben,it’s a good practice to mark many-to-one side as the owning side.

Mit anderen Worten,Items wäre derowning side andCart die umgekehrte Seite, genau das haben wir zuvor getan.

Wie haben wir das erreicht?

Indem wir das AttributmappedBy in die KlasseCartaufnehmen, markieren wir es als inverse Seite.

Gleichzeitig kommentieren wir das FeldItems.cart mit@ManyToOne, wodurchItems zur Besitzerseite wird.

Zurück zu unserem Beispiel „Inkonsistenz“: Hibernate weiß jetzt, dassitem2‘s reference is more important and will save item2‘s reference to the database.

Überprüfen wir das Ergebnis:

item1 ID=1, Foreign Key Cart ID=1
item2 ID=2, Foreign Key Cart ID=2

Obwohlcartin unserem Snippet aufitem2verweist, wird der Verweis vonitem2aufcart2in der Datenbank gespeichert.

6.3. Cart als Besitzerseite

Es ist auch möglich, die Seite vonone-to-manyals Besitzerseite und die Seite vonmany-to-oneals umgekehrte Seite zu markieren.

Obwohl dies keine empfohlene Vorgehensweise ist, probieren wir es einfach aus.

Das folgende Codefragment zeigt die Implementierung derone-to-many-Seite als Besitzerseite:

public class ItemsOIO {

    //  ...
    @ManyToOne
    @JoinColumn(name = "cart_id", insertable = false, updatable = false)
    private CartOIO cart;
    //..
}

public class CartOIO {

    //..
    @OneToMany
    @JoinColumn(name = "cart_id") // we need to duplicate the physical information
    private Set items;
    //..
}

Beachten Sie, wie wir das ElementmappedBy entfernt undmany-to-one @JoinColumn alsinsertable undupdatable auffalse gesetzt haben.

Wenn wir den gleichen Code ausführen, ist das Ergebnis das Gegenteil:

item1 ID=1, Foreign Key Cart ID=1
item2 ID=2, Foreign Key Cart ID=1

Wie oben gezeigt, gehört jetztitem2 zucart.

7. Fazit

Wir haben gesehen, wie einfach es ist, die Beziehung vonone-to-manyzur Hibernate ORM- und H2-Datenbank mithilfe von JPA-Annotationen zu implementieren.

Außerdem haben wir uns mit bidirektionalen Beziehungen und der Implementierung des Begriffs der Besitzerseite vertraut gemacht.

Der Quellcode dieses Tutorials befindet sich inover on GitHub.