Tutoriel Annotation Hibernate Un à Plusieurs

Tutoriel Annotation Hibernate Un à Plusieurs

1. introduction

Ce rapide tutoriel Hibernate nous emmènera à travers un exemple de mappageone-to-many utilisant des annotations JPA - une alternative à XML.

Nous allons également découvrir ce que sont les relations bidirectionnelles, comment elles peuvent créer des incohérences et comment l'idée de l'analyseownership peut aider.

Lectures complémentaires:

Botte de printemps avec Hibernate

Une introduction rapide et pratique à l'intégration de Spring Boot et Hibernate / JPA.

Read more

Présentation des identifiants dans Hibernate

Apprenez à mapper les identificateurs d'entité avec Hibernate.

Read more

2. La description

En termes simples,one-to-many mapping means that one row in a table is mapped to multiple rows in another table.

Examinons le diagramme de relations d'entité suivant pour voir une associationone-to-many:

image

Pour cet exemple, nous allons implémenter un système de panier, dans lequel nous avons une table pour chaque panier et une autre table pour chaque article. One cart can have many items, so here we have a one-to-many mapping.

La façon dont cela fonctionne au niveau de la base de données est que nous avonscart_id comme clé primaire dans la tablecart et aussicart_id  comme clé étrangère dansitems.

Et, la façon dont nous le faisons dans le code est avec@OneToMany.

Mappons la classeCart à l'objetItems de manière à refléter la relation dans la base de données:

public class Cart {

    //...

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

    //...
}

Nous pouvons également ajouter une référence àCart dansItems en utilisant@ManyToOne, ce qui en fait une relationbidirectional. Bidirectional signifie quewe are able to access items from carts, and also carts from items.

La propriétémappedBy est ce que nous utilisons pour indiquer à Hibernate quelle variable nous utilisons pour représenter la classe parente dans notre classe enfant.

Les technologies et bibliothèques suivantes sont utilisées pour développer un exemple d'application Hibernate qui implémente l'associationone-to-many:

  • JDK 1.8 ou version ultérieure

  • Hibernate 5

  • Maven 3 ou version ultérieure

  • Base de données H2

3. Installer

3.1. Configuration de la base de données

Voici notre script de base de données pour les tablesCart etItems. Nous utilisons la contrainte de clé étrangère pour le mappage deone-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;

La configuration de notre base de données étant prête, passons à la création du projet exemple Hibernate.

3.2. Dépendances Maven

Nous ajouterons ensuite les dépendances des pilotes Hibernate et H2 à notre fichierpom.xml. La dépendance Hibernate utilise la journalisation JBoss et elle est automatiquement ajoutée en tant que dépendances transitives:

  • Mise en veille prolongée version 5.2.7.Final

  • Version du pilote H2 1.4.197

Veuillez visiter le référentiel central Maven pour les dernières versions deHibernate et les dépendances deH2.

3.3. Configuration de mise en veille prolongée

Voici la configuration d'Hibernate:


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

3.4. ClasseHibernateAnnotationUtil

Avec la classeHibernateAnnotationUtil, il suffit de référencer le nouveau fichier de configuration Hibernate:

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. Les modèles

Les configurations liées au mappage seront effectuées à l'aide d'annotations JPA dans les classes de modèle:

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

    //...

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

    // getters and setters
}

Veuillez noter que l'annotation@OneToMany est utilisée pour définir la propriété dans la classeItems qui sera utilisée pour mapper la variablemappedBy. C'est pourquoi nous avons une propriété nommée "cart" dans la classeItems:

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

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

    public Items() {}

    // getters and setters
}

Il est important de noter que l'annotation@ManyToOne est associée à la variable de classeCart. L'annotation@JoinColumn fait référence à la colonne mappée.

5. En action

Dans le programme de test, nous créons une classe avec une méthodemain () pour obtenir la session Hibernate et enregistrer les objets du modèle dans la base de données implémentant l'associationone-to-many:

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());

Voici le résultat de notre programme de test:

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. L'annotation@ManyToOne

Comme nous l'avons vu dans la section 2, nous pouvons spécifier une relationmany-to-one en utilisant l'annotation@ManyToOne. Un mappagemany-to-one signifie que de nombreuses instances de cette entité sont mappées à une instance d'une autre entité -many items in one cart.

The @ManyToOne annotation lets us create bidirectional relationships, too. Nous aborderons cela en détail dans les prochaines sous-sections.

6.1. Incohérences et propriété

Maintenant, siCart faisait référence àItems, mais queItems ne faisait pas à son tour référence àCart,our relationship would be unidirectional. The objects would also have a natural consistency.

Dans notre cas, cependant, la relation est bidirectionnelle,bringing in the possibility of inconsistency.

Imaginons une situation où un développeur veut ajouteritem1 àcart etitem2 àcart2, mais fait une erreur de sorte que les références entrecart2 etitem2 deviennent incohérents:

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!

Comme indiqué ci-dessus,item2 fait référence àcart2 w alors quecart2  ne fait pas référence àitem2 — et c'est mauvais.

How should Hibernate save item2 to the database? La clé étrangèreitem2 référencera-t-ellecart1 oucart2?

Nous résolvons cette ambiguïté en utilisant l'idée d'unowning side de la relation - les références appartenant au côté propriétaire sont prioritaires et sont enregistrées dans la base de données.

6.2. items en tant que propriétaire

Comme indiqué dans lesJPA specification de la section 2.9,it’s a good practice to mark many-to-one side as the owning side.

En d'autres termes,Items erait le sableowning side Cart le côté inverse, ce qui est exactement ce que nous avons fait plus tôt.

Alors, comment avons-nous réalisé cela?

En incluant l'attributmappedBy dans la classeCart, nous le marquons comme le côté inverse.

Dans le même temps, nous annotons également le champItems.cart avec@ManyToOne, faisant deItems le côté propriétaire.

Pour en revenir à notre exemple «d'incohérence», Hibernate sait maintenant que lesitem2‘s reference is more important and will save item2‘s reference to the database.

Vérifions le résultat:

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

Bien quecart référenceitem2 dans notre extrait de code, la référence deitem2 àcart2 est enregistrée dans la base de données.

6.3. Cart en tant que propriétaire

Il est également possible de marquer le côtéone-to-many comme côté propriétaire et le côtémany-to-one comme côté inverse.

Bien que ce ne soit pas une pratique recommandée, allons-y et essayons-la.

L'extrait de code ci-dessous montre l'implémentation du côtéone-to-many comme côté propriétaire:

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

Remarquez comment nous avons supprimé l'élémentmappedBy et défini lesmany-to-one @JoinColumn commeinsertable etupdatable surfalse.

Si nous courons le même code, le résultat sera le contraire:

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

Comme indiqué ci-dessus, maintenantitem2 appartient àcart.

7. Conclusion

Nous avons vu à quel point il est facile d'implémenter la relationone-to-many avec la base de données Hibernate ORM et H2 en utilisant les annotations JPA.

Nous avons également appris sur les relations bidirectionnelles et sur la manière de mettre en œuvre la notion de propriétaire.

Le code source de ce tutoriel peut être trouvéover on GitHub.