Ejemplo de mapeo de Hibernate One To Many con anotaciones

Hoy vamos a explorar el mapeo uno a muchos en Hibernate. Veremos un ejemplo de mapeo uno a muchos en Hibernate utilizando anotaciones y configuración XML.

Mapeo Uno a Muchos en Hibernate

En términos simples, el mapeo uno a muchos significa que una fila en una tabla puede estar vinculada a múltiples filas en otra tabla. Por ejemplo, piensa en un sistema de carrito donde tenemos otra tabla para los elementos. Un carrito puede tener varios elementos, por lo que aquí tenemos un mapeo uno a muchos. Utilizaremos el escenario de Carrito-Elementos para nuestro ejemplo de mapeo uno a muchos en Hibernate.

Mapeo Uno a Muchos en Hibernate – Configuración de la Base de Datos

Podemos utilizar una restricción de clave externa para el mapeo uno a muchos. A continuación se muestra nuestro script de base de datos para las tablas Cart e Items. Estoy utilizando la base de datos MySQL para este ejemplo de mapeo uno a muchos en Hibernate. setup.sql

CREATE TABLE `Cart` (
  `cart_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `total` decimal(10,0) NOT NULL,
  `name` varchar(10) DEFAULT NULL,
  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,
  `item_id` varchar(10) NOT NULL,
  `item_total` decimal(10,0) NOT NULL,
  `quantity` int(3) 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;

A continuación se muestra el diagrama ER de las tablas Cart e Items. Nuestra configuración de base de datos está lista, pasemos a crear un ejemplo de proyecto de mapeo uno a muchos con Hibernate. En primer lugar, utilizaremos una configuración basada en XML y luego implementaremos el mapeo uno a muchos usando las anotaciones Hibernate y JPA.

Estructura del Proyecto de Mapeo Uno a Muchos con Hibernate

Crea un proyecto Maven simple en Eclipse o tu IDE favorito, la estructura final del proyecto se verá como en la siguiente imagen.

Dependencias de Maven para Hibernate

Nuestro archivo pom.xml final contiene dependencias para Hibernate y el controlador MySQL. Hibernate utiliza el registro de JBoss y se agrega automáticamente como dependencia transitiva.

<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.journaldev.hibernate</groupId>
  <artifactId>HibernateOneToManyMapping</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  
  <dependencies>
  	<dependency>
  		<groupId>org.hibernate</groupId>
  		<artifactId>hibernate-core</artifactId>
  		<version>4.3.5.Final</version>
  	</dependency>
  	<dependency>
  		<groupId>mysql</groupId>
  		<artifactId>mysql-connector-java</artifactId>
  		<version>5.0.5</version>
  	</dependency>
  </dependencies>
  
</project>

Nota que estoy utilizando la última versión de Hibernate 4.3.5.Final y la versión del controlador de MySQL basada en la instalación de mi base de datos.

Clases de Modelado de Mapeo Uno a Muchos en Hibernate

Para nuestras tablas Cart e Items, tenemos clases de modelo para reflejarlas. Cart.java

package com.journaldev.hibernate.model;

import java.util.Set;

public class Cart {

	private long id;
	private double total;
	private String name;
	private Set<Items> items;
	
	public long getId() {
		return id;
	}
	public void setId(long id) {
		this.id = id;
	}
	public double getTotal() {
		return total;
	}
	public void setTotal(double total) {
		this.total = total;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public Set<Items> getItems() {
		return items;
	}
	public void setItems(Set<Items> items) {
		this.items = items;
	}
	
}

I am using Set of Items, so that every record is unique. We can also use List or Array for one to many mapping in hibernate. Items.java

package com.journaldev.hibernate.model;

public class Items {

	private long id;
	private String itemId;
	private double itemTotal;
	private int quantity;
	private Cart cart;
	
	//Hibernate requiere un constructor sin argumentos
	public Items(){}
	
	public Items(String itemId, double total, int qty, Cart c){
		this.itemId=itemId;
		this.itemTotal=total;
		this.quantity=qty;
		this.cart=c;
	}
	public String getItemId() {
		return itemId;
	}
	public void setItemId(String itemId) {
		this.itemId = itemId;
	}
	public double getItemTotal() {
		return itemTotal;
	}
	public void setItemTotal(double itemTotal) {
		this.itemTotal = itemTotal;
	}
	public int getQuantity() {
		return quantity;
	}
	public void setQuantity(int quantity) {
		this.quantity = quantity;
	}
	public Cart getCart() {
		return cart;
	}
	public void setCart(Cart cart) {
		this.cart = cart;
	}
	public long getId() {
		return id;
	}
	public void setId(long id) {
		this.id = id;
	}
	
}

Los Items tienen una relación de muchos a uno con Cart, por lo que no necesitamos tener una colección para el objeto Cart.

Clase de Utilidad para la Fábrica de Sesiones en Hibernate

Tenemos una clase de utilidad para crear la Fábrica de Sesiones de Hibernate. HibernateUtil.java

package com.journaldev.hibernate.util;

import org.hibernate.SessionFactory;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;

public class HibernateUtil {

	private static SessionFactory sessionFactory;
	
	private static SessionFactory buildSessionFactory() {
        try {
            // Crea la Fábrica de Sesiones a partir de hibernate.cfg.xml
        	Configuration configuration = new Configuration();
        	configuration.configure("hibernate.cfg.xml");
        	System.out.println("Hibernate Configuration loaded");
        	
        	ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties()).build();
        	System.out.println("Hibernate serviceRegistry created");
        	
        	SessionFactory sessionFactory = configuration.buildSessionFactory(serviceRegistry);
        	
            return sessionFactory;
        }
        catch (Throwable ex) {
            System.err.println("Initial SessionFactory creation failed." + ex);
            ex.printStackTrace();
            throw new ExceptionInInitializerError(ex);
        }
    }
	
	public static SessionFactory getSessionFactory() {
		if(sessionFactory == null) sessionFactory = buildSessionFactory();
        return sessionFactory;
    }
}

Archivo XML de Configuración de Hibernate

Nuestro archivo de configuración xml de Hibernate contiene información de la base de datos y detalles de recursos de mapeo. hibernate.cfg.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
		"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
		"https://hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
        <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="hibernate.connection.password">pankaj123</property>
        <property name="hibernate.connection.url">jdbc:mysql://localhost/TestDB</property>
        <property name="hibernate.connection.username">pankaj</property>
        <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
        
        <property name="hibernate.current_session_context_class">thread</property>
        <property name="hibernate.show_sql">true</property>
        
        <mapping resource="cart.hbm.xml"/>
        <mapping resource="items.hbm.xml"/>
    </session-factory>
</hibernate-configuration>

Ejemplo de Mapeo Uno a Muchos en Hibernate – Configuración XML

Esta es la parte más importante del tutorial, veamos cómo tenemos que mapear tanto las clases Cart como Items para uno a muchos en hibernate. cart.hbm.xml

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
	"https://hibernate.org/dtd/hibernate-mapping-3.0.dtd">
	
<hibernate-mapping package="com.journaldev.hibernate.model">
	<class name="Cart" table="CART" >
		<id name="id" type="long">
			<column name="cart_id" />
			<generator class="identity" />
		</id>
		<property name="total" type="double">
			<column name="total" />
		</property>
		<property name="name" type="string">
			<column name="name" />
		</property>
		<set name="items" table="ITEMS" fetch="select">
			<key>
				<column name="cart_id" not-null="true"></column>
			</key>
			<one-to-many class="Items"/>
		</set>
	</class>
	
</hibernate-mapping>

La parte importante es el elemento set y el elemento one-to-many dentro de él. Note que estamos proporcionando la clave a ser usada para el mapeo uno a muchos, es decir, cart_id. items.hbm.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" 
"https://hibernate.org/dtd/hibernate-mapping-3.0.dtd" >

<hibernate-mapping package="com.journaldev.hibernate.model">

	<class name="Items" table="ITEMS">
		<id name="id" type="long">
			<column name="id" />
			<generator class="identity" />
		</id>
		<property name="itemId" type="string">
			<column name="item_id"></column>
		</property>
		<property name="itemTotal" type="double">
			<column name="item_total"></column>
		</property>
		<property name="quantity" type="integer">
			<column name="quantity"></column>
		</property>
		
		<many-to-one name="cart" class="Cart">
			<column name="cart_id" not-null="true"></column>
		</many-to-one>
	</class>

</hibernate-mapping>

Note que de items a cart, es una relación de muchos a uno. Entonces necesitamos usar el elemento many-to-one para cart y estamos proporcionando el nombre de la columna que será mapeada con la clave. Así que, basado en la configuración de mapeo de hibernate de Cart, su clave cart_id será usada para el mapeo. Nuestro proyecto para el ejemplo de Mapeo Uno a Muchos en Hibernate usando mapeo XML está listo, escribamos un programa de prueba y veamos si funciona bien o no.

Ejemplo de Mapeo Uno a Muchos en Hibernate – Programa de Prueba

HibernateOneToManyMain.java

package com.journaldev.hibernate.main;

import java.util.HashSet;
import java.util.Set;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;

import com.journaldev.hibernate.model.Cart;
import com.journaldev.hibernate.model.Items;
import com.journaldev.hibernate.util.HibernateUtil;

public class HibernateOneToManyMain {

	public static void main(String[] args) {

		Cart cart = new Cart();
		cart.setName("MyCart");
		
		Items item1 = new Items("I1", 10, 1, cart);
		Items item2 = new Items("I2", 20, 2, cart);
		Set itemsSet = new HashSet();
		itemsSet.add(item1); itemsSet.add(item2);
		
		cart.setItems(itemsSet);
		cart.setTotal(10*1 + 20*2);
		
		SessionFactory sessionFactory = null;
		Session session = null;
		Transaction tx = null;
		try{
		//Obtener Sesión
		sessionFactory = HibernateUtil.getSessionFactory();
		session = sessionFactory.getCurrentSession();
		System.out.println("Session created");
		//Iniciar transacción
		tx = session.beginTransaction();
		
		//Guardar los objetos del modelo
		session.save(cart);
		session.save(item1);
		session.save(item2);
		
		//Confirmar transacción
		tx.commit();
		System.out.println("Cart ID="+cart.getId());
		
		}catch(Exception e){
			System.out.println("Exception occured. "+e.getMessage());
			e.printStackTrace();
		}finally{
			if(!sessionFactory.isClosed()){
				System.out.println("Closing SessionFactory");
				sessionFactory.close();
			}
		}
	}

}

Observa que necesitamos guardar tanto los objetos Carro como los objetos Artículos uno por uno. Hibernate se encargará de actualizar las claves foráneas en la tabla de Artículos. Cuando ejecutamos el programa anterior, obtenemos la siguiente salida.

Hibernate Configuration loaded
Hibernate serviceRegistry created
Session created
Hibernate: insert into CART (total, name) values (?, ?)
Hibernate: insert into ITEMS (item_id, item_total, quantity, cart_id) values (?, ?, ?, ?)
Hibernate: insert into ITEMS (item_id, item_total, quantity, cart_id) values (?, ?, ?, ?)
Hibernate: update ITEMS set cart_id=? where id=?
Hibernate: update ITEMS set cart_id=? where id=?
Cart ID=6
Closing SessionFactory

Observa que Hibernate está utilizando una consulta de actualización para establecer el cart_id en la tabla ITEMS.

Mapeo de Uno a Muchos de Hibernate con Anotaciones

Ahora que hemos visto cómo implementar el mapeo de Uno a Muchos en Hibernate utilizando configuraciones basadas en XML, veamos cómo podemos hacer lo mismo usando anotaciones JPA.

Ejemplo de Mapeo de Uno a Muchos de Hibernate con Anotaciones

El archivo de configuración de Hibernate es casi el mismo, excepto que el elemento de mapeo cambia porque estamos utilizando clases para el mapeo de uno a muchos de Hibernate usando anotaciones. hibernate-annotation.cfg.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
		"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
		"https://hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
        <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="hibernate.connection.password">pankaj123</property>
        <property name="hibernate.connection.url">jdbc:mysql://localhost/TestDB</property>
        <property name="hibernate.connection.username">pankaj</property>
        <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
        
        <property name="hibernate.current_session_context_class">thread</property>
        <property name="hibernate.show_sql">true</property>
        
        <mapping class="com.journaldev.hibernate.model.Cart1"/>
        <mapping class="com.journaldev.hibernate.model.Items1"/>
    </session-factory>
</hibernate-configuration>

Clase de Utilidad de SessionFactory de Hibernate

La clase de utilidad de SessionFactory es casi la misma, solo necesitamos usar el nuevo archivo de configuración de Hibernate. HibernateAnnotationUtil.java

package com.journaldev.hibernate.util;

import org.hibernate.SessionFactory;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;

public class HibernateAnnotationUtil {

	private static SessionFactory sessionFactory;
	
	private static SessionFactory buildSessionFactory() {
        try {
            // Cree la SessionFactory desde hibernate-annotation.cfg.xml
        	Configuration configuration = new Configuration();
        	configuration.configure("hibernate-annotation.cfg.xml");
        	System.out.println("Hibernate Annotation Configuration loaded");
        	
        	ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties()).build();
        	System.out.println("Hibernate Annotation serviceRegistry created");
        	
        	SessionFactory sessionFactory = configuration.buildSessionFactory(serviceRegistry);
        	
            return sessionFactory;
        }
        catch (Throwable ex) {
            System.err.println("Initial SessionFactory creation failed." + ex);
            ex.printStackTrace();
            throw new ExceptionInInitializerError(ex);
        }
    }
	
	public static SessionFactory getSessionFactory() {
		if(sessionFactory == null) sessionFactory = buildSessionFactory();
        return sessionFactory;
    }
}

Clases de modelo de anotaciones de mapeo de uno a muchos de Hibernate

Dado que no tenemos archivos de mapeo basados en xml, todas las configuraciones relacionadas con el mapeo se realizarán utilizando anotaciones JPA en las clases de modelo. Si comprende el mapeo basado en xml, es muy simple y similar. Cart1.java

package com.journaldev.hibernate.model;

import java.util.Set;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;

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

	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	@Column(name="cart_id")
	private long id;
	
	@Column(name="total")
	private double total;
	
	@Column(name="name")
	private String name;
	
	@OneToMany(mappedBy="cart1")
	private Set items1;
	
// Métodos Getter Setter para propiedades
}

El punto importante a tener en cuenta es la anotación OneToMany donde se utiliza la variable mappedBy para definir la propiedad en la clase Items1 que se utilizará con fines de mapeo. Por lo tanto, deberíamos tener una propiedad llamada “cart1” en la clase Items1. No olvide incluir todos los métodos getter-setter. Items1.java

package com.journaldev.hibernate.model;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

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

	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	@Column(name="id")
	private long id;
	
	@Column(name="item_id")
	private String itemId;
	
	@Column(name="item_total")
	private double itemTotal;
	
	@Column(name="quantity")
	private int quantity;
	
	@ManyToOne
	@JoinColumn(name="cart_id", nullable=false)
	private Cart1 cart1;
	
	// Hibernate requiere constructor sin argumentos
	public Items1(){}
	
	public Items1(String itemId, double total, int qty, Cart1 c){
		this.itemId=itemId;
		this.itemTotal=total;
		this.quantity=qty;
		this.cart1=c;
	}
// Métodos Getter Setter
}

El punto más importante en la clase anterior es la anotación ManyToOne en la variable de la clase Cart1 y la anotación JoinColumn para proporcionar el nombre de la columna para el mapeo. Eso es todo para el mapeo uno a muchos en hibernate usando anotaciones en clases de modelo. Compárelo con las configuraciones basadas en XML, las encontrará muy similares. Vamos a escribir un programa de prueba y ejecutarlo.

Ejemplo de Programa de Prueba de Mapeo Uno a Muchos con Anotaciones de Hibernate

Nuestro programa de prueba es similar a la configuración basada en xml, simplemente estamos utilizando las nuevas clases para obtener la Sesión de Hibernate y guardar los objetos de modelo en la base de datos. HibernateOneToManyAnnotationMain.java

package com.journaldev.hibernate.main;

import java.util.HashSet;
import java.util.Set;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;

import com.journaldev.hibernate.model.Cart1;
import com.journaldev.hibernate.model.Items1;
import com.journaldev.hibernate.util.HibernateAnnotationUtil;

public class HibernateOneToManyAnnotationMain {

	public static void main(String[] args) {

		Cart1 cart = new Cart1();
		cart.setName("MyCart1");
		
		Items1 item1 = new Items1("I10", 10, 1, cart);
		Items1 item2 = new Items1("I20", 20, 2, cart);
		Set itemsSet = new HashSet();
		itemsSet.add(item1); itemsSet.add(item2);
		
		cart.setItems1(itemsSet);
		cart.setTotal(10*1 + 20*2);
		
		SessionFactory sessionFactory = null;
		Session session = null;
		Transaction tx = null;
		try{
		//Obtener Sesión
		sessionFactory = HibernateAnnotationUtil.getSessionFactory();
		session = sessionFactory.getCurrentSession();
		System.out.println("Session created");
		//Iniciar transacción
		tx = session.beginTransaction();
		//Guardar el objeto de modelo
		session.save(cart);
		session.save(item1);
		session.save(item2);
		//Confirmar transacción
		tx.commit();
		System.out.println("Cart1 ID="+cart.getId());
		System.out.println("item1 ID="+item1.getId()+", Foreign Key Cart ID="+item1.getCart1().getId());
		System.out.println("item2 ID="+item2.getId()+", Foreign Key Cart ID="+item1.getCart1().getId());
		
		}catch(Exception e){
			System.out.println("Exception occured. "+e.getMessage());
			e.printStackTrace();
		}finally{
			if(!sessionFactory.isClosed()){
				System.out.println("Closing SessionFactory");
				sessionFactory.close();
			}
		}
	}

}

Cuando ejecutamos el programa de prueba de ejemplo de mapeo uno a muchos de hibernate anterior, obtenemos la siguiente salida.

Hibernate Annotation Configuration loaded
Hibernate Annotation serviceRegistry created
Session created
Hibernate: insert into CART (name, total) values (?, ?)
Hibernate: insert into ITEMS (cart_id, item_id, item_total, quantity) values (?, ?, ?, ?)
Hibernate: insert into ITEMS (cart_id, item_id, item_total, quantity) values (?, ?, ?, ?)
Cart1 ID=7
item1 ID=9, Foreign Key Cart ID=7
item2 ID=10, Foreign Key Cart ID=7
Closing SessionFactory

Eso es todo para el mapeo uno a muchos de hibernate, descargue el proyecto de ejemplo desde el siguiente enlace y haga algunos experimentos más.

Descargar Proyecto de Mapeo Hibernate OneToMany

Source:
https://www.digitalocean.com/community/tutorials/hibernate-one-to-many-mapping-annotation