1. Introducción
Este tutorial rápido de Hibernate nos llevará a través de un ejemplo de un mapeo de uno a muchos usando anotaciones JPA, una alternativa a XML.
También aprenderemos qué son las relaciones bidireccionales, cómo pueden crear inconsistencias y cómo puede ayudar la idea de propiedad.
2. Descripción
En pocas palabras, la asignación de uno a muchos significa que una fila de una tabla se asigna a varias filas de otra tabla.
Veamos el siguiente diagrama de relación de entidades para ver una asociación de uno a varios :

Para este ejemplo, implementaremos un sistema de carrito donde tenemos una mesa para cada carrito y otra mesa para cada artículo. Un carrito puede tener muchos artículos, por lo que aquí tenemos un mapeo de uno a muchos .
La forma en que esto funciona a nivel de la base de datos es que tenemos un cart_id como clave principal en la tabla del carrito y también un cart_id como clave externa en los artículos .
La forma en que lo hacemos en código es con @OneToMany .
Asignemos la clase Cart al objeto Items de una manera que refleje la relación en la base de datos:
public class Cart { //... @OneToMany(mappedBy="cart") private Set items; //... }
También podemos agregar una referencia al carrito en artículos usando @ManyToOne , lo que hace que esta sea una relación bidireccional. Bidireccional significa que podemos acceder a los artículos de los carros y también a los carros de los artículos .
La propiedad mappedBy es lo que usamos para decirle a Hibernate qué variable estamos usando para representar la clase padre en nuestra clase hija.
Las siguientes tecnologías y bibliotecas se utilizan para desarrollar una aplicación de muestra de Hibernate que implementa la asociación de uno a muchos :
- JDK 1.8 o posterior
- Hibernar 5
- Maven 3 o posterior
- Base de datos H2
3. Configuración
3.1. Configuración de la base de datos
A continuación se muestra nuestro script de base de datos para las tablas de Carrito y Artículos . Usamos la restricción de clave externa para el mapeo de uno a muchos :
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 configuración de nuestra base de datos está lista, así que pasemos a crear el proyecto de ejemplo de Hibernate.
3.2. Dependencias de Maven
Luego agregaremos las dependencias de los controladores Hibernate y H2 a nuestro archivo pom.xml . La dependencia de Hibernate usa el registro de JBoss y se agrega automáticamente como dependencias transitivas:
- Hibernate versión 5 .2.7.Final
- Controlador H2 versión 1 .4.197
Visite el repositorio central de Maven para obtener las últimas versiones de Hibernate y las dependencias H2.
3.3. Configuración de hibernación
Aquí está la configuración de Hibernate:
org.h2.Driver jdbc:h2:mem:spring_hibernate_one_to_many sa org.hibernate.dialect.H2Dialect thread true
3.4. HibernateAnnotationUtil ( clase)
Con la clase HibernateAnnotationUtil , solo necesitamos hacer referencia al nuevo archivo de configuración de 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. Los modelos
Las configuraciones relacionadas con el mapeo se realizarán utilizando anotaciones JPA en las clases del modelo:
@Entity @Table(name="CART") public class Cart { //... @OneToMany(mappedBy="cart") private Set items; // getters and setters }
Tenga en cuenta que la anotación @OneToMany se usa para definir la propiedad en la clase Items que se usará para mapear la variable mappedBy . Es por eso que tenemos una propiedad llamada " carrito " en la clase Artículos :
@Entity @Table(name="ITEMS") public class Items { //... @ManyToOne @JoinColumn(name="cart_id", nullable=false) private Cart cart; public Items() {} // getters and setters }
También es importante tener en cuenta que la anotación @ManyToOne está asociada con la variable de clase Cart . La anotación @JoinColumn hace referencia a la columna asignada.
5. En acción
En el programa de prueba, estamos creando una clase con un método main () para obtener la sesión de Hibernate y guardando los objetos del modelo en la base de datos implementando la asociación uno a muchos :
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 AnnotationAs 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.