Hibernate One to Many Annotation Tutorial

1. Einleitung

Dieses kurze Tutorial zum Ruhezustand führt uns durch ein Beispiel für eine Eins-zu-Viele- Zuordnung mithilfe von JPA-Anmerkungen, einer Alternative zu XML.

Wir werden auch lernen, was bidirektionale Beziehungen sind, wie sie zu Inkonsistenzen führen können und wie die Idee des Eigentums helfen kann.

2. Beschreibung

Einfach ausgedrückt bedeutet eine Eins-zu-Viele- Zuordnung, dass eine Zeile in einer Tabelle mehreren Zeilen in einer anderen Tabelle zugeordnet wird.

Schauen wir uns das folgende Entitätsbeziehungsdiagramm an, um eine Eins-zu-Viele- Assoziation zu sehen:

In diesem Beispiel implementieren wir ein Warenkorbsystem, in dem wir für jeden Einkaufswagen eine Tabelle und für jeden Artikel eine weitere Tabelle haben. Ein Einkaufswagen kann viele Artikel enthalten, daher haben wir hier eine Eins-zu-Viele- Zuordnung.

Auf Datenbankebene funktioniert dies so, dass wir eine cart_id als Primärschlüssel in der Cart- Tabelle und eine cart_id als Fremdschlüssel in Artikeln haben .

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

Ordnen wir die Cart- Klasse dem Items- Objekt so zu, dass sie die Beziehung in der Datenbank widerspiegelt:

public class Cart { //... @OneToMany(mappedBy="cart") private Set items; //... }

Wir können mit @ManyToOne auch einen Verweis auf Warenkorb in Artikeln hinzufügen , wodurch dies zu einer bidirektionalen Beziehung wird. Bidirektionale bedeutet , dass wir in der Lage sind , den Zugang Artikel von Karren , und auch Wagen aus Artikel .

Mit der Eigenschaft mappedBy teilen 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 eine Eins-zu-Viele- Zuordnung implementiert :

  • JDK 1.8 oder höher
  • Ruhezustand 5
  • Maven 3 oder höher
  • H2-Datenbank

3. Setup

3.1. Datenbank-Setup

Unten finden Sie unser Datenbankskript für Warenkorb- und Artikeltabellen . Wir verwenden die Fremdschlüsseleinschränkung für die Eins-zu-Viele- Zuordnung:

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;

Unser Datenbank-Setup ist fertig. Fahren wir also mit der Erstellung des Beispielprojekts für den Ruhezustand fort.

3.2. Maven-Abhängigkeiten

Anschließend fügen wir unserer Datei pom.xml die Treiberabhängigkeiten Hibernate und H2 hinzu . Die Hibernate-Abhängigkeit verwendet die JBoss-Protokollierung und wird automatisch als transitive Abhängigkeiten hinzugefügt:

  • Ruhezustand Version 5 .2.7.Final
  • H2-Treiber Version 1 .4.197

Die neuesten Versionen von Hibernate und die H2-Abhängigkeiten finden Sie im zentralen Maven-Repository.

3.3. Konfiguration im 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 Class

Bei der HibernateAnnotationUtil- Klasse müssen wir nur auf die neue Hibernate-Konfigurationsdatei 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 Modelle

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

@Entity @Table(name="CART") public class Cart { //... @OneToMany(mappedBy="cart") private Set items; // getters and setters }

Beachten Sie, dass die Annotation @OneToMany verwendet wird, um die Eigenschaft in der Items- Klasse zu definieren, die zum Zuordnen der Variablen mappedBy verwendet wird. Aus diesem Grund haben wir in der Items- Klasse eine Eigenschaft namens " cart " :

@Entity @Table(name="ITEMS") public class Items { //... @ManyToOne @JoinColumn(name="cart_id", nullable=false) private Cart cart; public Items() {} // getters and setters } 

Es ist auch wichtig zu beachten, dass die Annotation @ManyToOne der Cart- Klassenvariablen zugeordnet ist. Die Annotation @JoinColumn verweist auf die zugeordnete Spalte.

5. In Aktion

Im Testprogramm erstellen wir eine Klasse mit einer main () -Methode zum Abrufen der Hibernate-Sitzung und speichern die Modellobjekte in der Datenbank, wobei die Eins-zu-Viele- Zuordnung implementiert wird :

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("Cartitem1, Foreign Key Cartitem2, Foreign Key Cartmany-to-one">6. The @ManyToOne Annotation

As we have seen in section 2, we can specify a many-to-one relationship by using the @ManyToOne annotation. A many-to-one mapping means that many instances of this entity are mapped to one instance of another entity – many items in one cart.

The @ManyToOne annotation lets us create bidirectional relationships too. We'll cover this in detail in the next few subsections.

6.1. Inconsistencies and Ownership

Now, if Cart referenced Items, but Items didn't in turn reference Cart, our relationship would be unidirectional. The objects would also have a natural consistency.

In our case though, the relationship is bidirectional, bringing in the possibility of inconsistency.

Let's imagine a situation where a developer wants to add item1 to cart and item2 to cart2, but makes a mistake so that the references between cart2 and item2 become inconsistent:

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!

As shown above, item2 references cart2, whereas cart2 doesn't reference item2, and that's bad.

How should Hibernate save item2 to the database? Will item2 foreign key reference cart1 or cart2?

We resolve this ambiguity using the idea of an owning side of the relationship; references belonging to the owning side take precedence and are saved to the database.

6.2. Items as the Owning Side

As stated in the JPA specification under section 2.9, it's a good practice to mark many-to-one side as the owning side.

In other words, Items would be the owning side and Cart the inverse side, which is exactly what we did earlier.

So how did we achieve this?

By including the mappedBy attribute in the Cart class, we mark it as the inverse side.

At the same time, we also annotate the Items.cart field with @ManyToOne, making Items the owning side.

Going back to our “inconsistency” example, now Hibernate knows that the item2‘s reference is more important and will save item2‘s reference to the database.

Let's check the result:

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

Although cart references item2 in our snippet, item2‘s reference to cart2 is saved in the database.

6.3. Cart as the Owning Side

It's also possible to mark the one-to-many side as the owning side, and many-to-one side as the inverse side.

Although this is not a recommended practice, let's go ahead and give it a try.

The code snippet below shows the implementation of one-to-many side as the owning side:

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; //.. } 

Notice how we removed the mappedBy element and set the many-to-one @JoinColumn as insertable and updatable to false.

If we run the same code, the result will be the opposite:

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

As shown above, now item2 belongs to cart.

7. Conclusion

We have seen how easy it is to implement the one-to-many relationship with the Hibernate ORM and H2 database using JPA annotations.

Additionally, we learned about bidirectional relationships and how to implement the notion of an owning side.

The source code in this article can be found over on GitHub.